【bzoj2213】[Poi2011]Difference dp
题目描述
已知一个长度为n的由小写字母组成的字符串,求其中连续的一段,满足该段中出现最多的字母出现的个数减去该段中出现最少的字母出现的个数最大。求这个个数。
输入
第一行,n
第二行,该字符串
1<=n<=1000000
输出
一行,表示结果
样例输入
10
aabbaaabab
样例输出
3
题解
dp
令$sum[i][j]$表示前$i$个字母中$j$的出现次数,那么题目所求的是$Max((sum[r][i]-sum[l-1][i])-(sum[r][j]-sum[l-1][j]))=Max((sum[r][i]-sum[r][j])-(sum[l-1][i]-sum[l-1][j]))$
其中$j$必须在$[l,r]$中出现过,即$sum[r][j]-sum[l-1][j]>0$。
所以我们可以扫一遍右端点,要求的就是满足$sum[r][j]-sum[l-1][j]>0$的最小的$sum[l-1][i]-sum[l-1][j]$。
设$f[k][j]$表示当前的$sum[k]-sum[j]$,那么需要维护的就是$f[k][j]$的最小值和$f[k][j]$在$sum[j]$不同时的次小值。更新时如果能够用最小值更新答案则用最小值,否则用次小值。然后再用这个$f$去更新最、次小值。
所以同时还要记录最小值和次小值中$j$出现的次数。
因为每次加入一个字符,只有50个$f$值改变了,因此时间复杂度为$O(50n)$。
#include <cstdio> #include <algorithm> #define K 26 using namespace std; int sum[K] , f[K][K] , g[K][K] , c[K][K] , h[K][K] , d[K][K] , ans; char str[1000010]; void update(int a , int b) { if(c[a][b] < sum[b]) ans = max(ans , f[a][b] - g[a][b]); else if(d[a][b] < sum[b]) ans = max(ans , f[a][b] - h[a][b]); if(f[a][b] < g[a][b]) { if(c[a][b] != sum[b]) h[a][b] = g[a][b] , d[a][b] = c[a][b]; g[a][b] = f[a][b] , c[a][b] = sum[b]; } else if(c[a][b] != sum[b] && f[a][b] < h[a][b]) h[a][b] = f[a][b] , d[a][b] = sum[b]; } int main() { int n , i , j , t; scanf("%d%s" , &n , str + 1); for(i = 1 ; i <= n ; i ++ ) { t = str[i] - 'a' , sum[t] ++ ; for(j = 0 ; j < K ; j ++ ) if(t != j) f[t][j] ++ , update(t , j) , f[j][t] -- , update(j , t); } printf("%d\n" , ans); return 0; }