联考考试考到了这个题,随机化40分,现在来秒掉它吧。
题意:
给一个字符串,求其中的一段,使得出现次数最多的字符与出现次数最少的字符的出现次数之差最大。
输入输出样例
我们定义$cnt[i][j]$表示区间$[1,i]$中,j出现的次数,
定义字符a,b(非字符a,b),a为出现最多的字符,b为出现最少的字符。
我们利用前缀和统计每一个字符出现的次数。
那么对于一个区间$[i,j]$,字符a出现的次数为$cnt[j][a]-cnt[i][a]$,字符b出现的次数为$cnt[j][b]-cnt[i][b]$,我们枚举每一个字符的配对情况。
对于26个字符$$ans=max \{ ans,(cnt[j][a]-cnt[i][a])-(cnt[j][b]-cnt[i][b]) \}$$。
这样枚举时间复杂度$O(n^2 \times 26 \times 26)$,还可以啦。
现在我们来优化一下上边的过程。
$$(cnt[j][a]-cnt[i][a])-cnt[j][b]-(cnt[i][b])$$
可以变为
$$(cnt[j][a]-cnt[j][b])-(cnt[i][a]-cnt[i][b])$$
对于算式的前半边O(n \times 26)枚举,我们来优化一下后半边。
对于后边的式子,求得为j以前$cnt[i][a]-cnt[i][b]$的最小值,然而j是怎么过来的,到了j必然前边的数都有经过,所以我们在枚举是维护一个$cnt[i][a]-cnt[i][b]$的最小值即可(代码中用到minv数组)。
代码中有p[i][j]数组表示更新字符i和字符j差的最小值的位置。
总的时间复杂度$O(n \times 26)$
#include<complex> #include<cstdio> using namespace std; const int N=1e6+7,M=27; int n,ans; int last[M],num[M],p[M][M],minv[M][M]; //last表示字符最后出现的位置,num表示字符出现的次数。 //p数组表示更新维护最小值的位置,minv表示维护的最小值。 char s[N]; int main() { // freopen("B.in","r",stdin); // freopen("B.out","w",stdout); scanf("%d%s",&n,s+1); for(int i=1;i<=n;i++) { int c=s[i]-'a'; num[c]++;last[c]=i; for(int j=0;j<26;j++) if(j!=c && num[j]) ans=max(ans,max(num[c]-num[j]-minv[c][j]-(last[j]==p[c][j]) ,num[j]-num[c]-minv[j][c]-(last[c]==p[j][c]))); //这个判断:如果成立,那么那个点属于前一段区间。 for(int j=0;j<26;j++) if(num[j]-num[c]<minv[j][c]) { minv[j][c]=num[j]-num[c]; p[j][c]=i; } //更新最小值。 } printf("%d\n",ans); // fclose(stdin);fclose(stdout); return 0; }