BZOJ2213: [Poi2011]Difference【"动态" DP】

22 篇文章 0 订阅
题目描述:

已知一个长度为n的由小写字母组成的字符串,求一个子串,出现最多的字母出现的个数减去该段中出现过的出现次数最少的字母出现的个数最大。求这个个数。

题目分析:
  • 法一:
    枚举是哪两种字母相减,设为a,b,那么就只需要把a,b从原串中拿出来,a的值+1,b的值-1,求最大子段和,虽然a,b并不一定是真实串中最多最少的字母,但是这样做的最大值一定是答案。每个位置最多拿出来52次,复杂度是 O ( 52 n ) O(52n) O(52n)

    注意求最大子段和时必须保证串中既有a又有b,即当 [ l , r ] [l,r] [l,r] l l l位置的字符为b时才能用存下来的最小值去更新使用的最小值,相当于延迟更新。

  • 法二:
    DP,设 s u m [ i ] [ j ] sum[i][j] sum[i][j]表示前 i i i个字符中字符 j j j的出现次数,那么 a n s = max ⁡ ( s u m [ r ] [ i ] − s u m [ l − 1 ] [ i ] − ( s u m [ r ] [ j ] − s u m [ l − 1 ] [ j ] ) ) = max ⁡ ( s u m [ r ] [ i ] − s u m [ r ] [ j ] − ( s u m [ l − 1 ] [ i ] − s u m [ l − 1 ] [ j ] ) ) ans=\max \big(sum[r][i]-sum[l-1][i]-(sum[r][j]-sum[l-1][j])\big)\\=\max \big(sum[r][i]-sum[r][j]-(sum[l-1][i]-sum[l-1][j])\big) ans=max(sum[r][i]sum[l1][i](sum[r][j]sum[l1][j]))=max(sum[r][i]sum[r][j](sum[l1][i]sum[l1][j]))
    考虑 r − 1 r-1 r1变为 r r r,r位置的字符为 p p p,记 f [ i ] [ j ] = s u m [ r ] [ i ] − s u m [ r ] [ j ] f[i][j]=sum[r][i]-sum[r][j] f[i][j]=sum[r][i]sum[r][j],同法一的延迟更新类似,此时存下的 f [ j ] [ p ] f[j][p] f[j][p]的最小值可以用来更新 m i n [ j ] [ p ] min[j][p] min[j][p]。然后只有 f [ p ] [ j ] f[p][j] f[p][j] f [ j ] [ p ] f[j][p] f[j][p]会变。然后更新对应的答案即可。

法一Code:

#include<bits/stdc++.h>
#define maxn 1000005
#define LL long long
using namespace std;
const int inf = 1<<30;
int n,ans;
char c[maxn];
vector<int>pos[26];
void solve(int i,int j){
	int sum=0,mn=inf,rmn=0;
	for(int x=0,y=0;x<pos[i].size()||y<pos[j].size();){
		if(y==pos[j].size()||(x<pos[i].size()&&pos[i][x]<pos[j][y])) sum++,x++;
		else sum--,mn=min(mn,rmn),y++;
		rmn=min(rmn,sum);
		if(mn!=inf) ans=max(ans,sum-mn);
	}
}
int main()
{
	scanf("%d%s",&n,c+1);
	for(int i=1;i<=n;i++) pos[c[i]-'a'].push_back(i);
	for(int i=0;i<26;i++) for(int j=0;j<26;j++) if(i!=j) solve(i,j);
	printf("%d\n",ans);
}

法二Code:

#include<bits/stdc++.h>
#define maxn 1000005
#define LL long long
using namespace std;
const int inf = 1<<30;
int n,mn[26][26],rmn[26][26],sum[26][26],ans;
char c[maxn];
int main()
{
	memset(mn,0x3f,sizeof mn);
	scanf("%d%s",&n,c+1);
	for(int i=1;i<=n;i++){
		int p=c[i]-'a';
		for(int j=0;j<26;j++) if(j!=p){
			sum[p][j]++,sum[j][p]--;
			mn[j][p]=min(mn[j][p],rmn[j][p]);
			rmn[j][p]=min(rmn[j][p],sum[j][p]);
			ans=max(ans,sum[p][j]-mn[p][j]);
			ans=max(ans,sum[j][p]-mn[j][p]);
		}
	}
	printf("%d\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值