一个字符串,由前k个字母组成,长度为m + n,其中前m个字符已经确定,后面n个由你自由选择,
使得这个串的不同的子序列的个数最多,空串也算一个子序列。
1 <= m <= 10^6,0 <= n <= 10^6,1 <= k <= 26
首先,我们考虑n = 0的情况,
问题就为给定一个字符串,求它有多少个不同的子序列。
pre[i]表示字母i最后出现的位置,初始化为0
f[i]表示以第i个字符结尾的与前面已经出现的子序列都不同的子序列个数
g[i] = ∑0<=j<=if[j]
则我们知道f[i] = ∑pre[i]<=j<=i-1f[j]
则 if pre[i] == 0 then f[i] = g[i-1]
if pre[i] > 0 then f[i] = g[i-1] - g[pre[str[i]]-1]
答案就是g[m]
那如果n > 0 呢?
从f的递推式我们知道,要使得f[i]最大,i处填的字符应该是pre值最小的i
那么我们接着遍历,每次拿pre最小的字符,更新f和pre值
最终答案就是g[n + m]
代码:
//File Name: cf645E.cpp //Created Time: 2017年01月05日 星期四 13时53分24秒 #include <bits/stdc++.h> #define LL long long #define fir first #define sec second #define pii pair<int,int> using namespace std; const int MAXN = 2000000 + 2; const int P = (int)1e9 + 7; LL f[MAXN],g[MAXN]; char str[MAXN]; int pre[26]; set<pii> rem; LL solve(int n,int k){ int m = strlen(str + 1); memset(pre,0,sizeof(pre)); f[0] = g[0] = 1; for(int i=1;i<=m;++i){ int v = str[i] - 'a'; if(pre[v] == 0) f[i] = g[i - 1]; else f[i] = (g[i - 1] - g[pre[v] - 1] + P) % P; g[i] = (g[i - 1] + f[i]) % P; pre[v] = i; // printf("i = %d f = %lld g = %lld ",i,f[i],g[i]); } rem.clear(); for(int i=0;i<k;++i) rem.insert(pii(pre[i],i)); for(int i=m+1;i<=m+n;++i){ // puts("ffff"); pii now = *rem.begin(); int pos = now.fir,v = now.sec; if(pos == 0) f[i] = g[i - 1]; else f[i] = (g[i - 1] - g[pos - 1] + P) % P; g[i] = (g[i - 1] + f[i]) % P; pre[v] = i; rem.erase(rem.begin()); rem.insert(pii(i,v)); } // for(int i=0;i<=m+n;++i) // printf("i = %d f = %lld ",i,f[i]); return g[m + n]; } int main(){ int n,k; scanf("%d %d",&n,&k); scanf("%s",str + 1); printf("%lld ",solve(n,k)); return 0; }