给一个长为 (m) 的字符串 (S) ,你需要用第 (1) 到 (k) 个小写字母构造一个长为 (n) 的字符串 (S') ,使得 (S+S') 本质不同的子串个数最多
输出子串个数 (pmod {10^9+7})
(n, mleq10^6, kleq26)
动态规划,贪心
考虑如何求出一个串本质不同的子串个数
设计一个线性dp,令 (f_i) 为 (S) 前 (i) 位所构成的子串个数
第 (i) 位有取或不取两种状态,因此若不考虑去重, (f_i=2f_{i-1})
若选取第 (i) 位,计算重复的部分为以这个字符上一次出现的位置为结尾的子串个数(想一想,为什么)
综上所述 (f_i=2f_{i-1}-f_{lst_i-1})
对于构造字符串 (S') ,显然要让 (f_{lst_i-1}) 尽量小,由于 (f_i) 单调递增,于是只需让 (lst_i) 尽量小即可,可以用堆维护(实际上直接暴力 (O(k)) 常数还小一点
时间复杂度 (O(nlog k))
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair <int, int> pii;
const int maxn = 1e6 + 10, P = 1e9 + 7;
char str[maxn];
int n, m, k, lst[26], dp_buf[maxn << 1], *f = dp_buf + 1;
inline int add(int a, int b) {
a += b; return a < P ? a : a - P;
}
inline int dec(int a, int b) {
a -= b; return a < 0 ? a + P : a;
}
int main() {
scanf("%d %d %s", &n, &k, str + 1);
m = strlen(str + 1);
f[0] = 1;
for (int i = 1; i <= m; i++) {
f[i] = dec(add(f[i - 1], f[i - 1]), f[lst[str[i] - 'a'] - 1]);
lst[str[i] - 'a'] = i;
}
static priority_queue <int, vector <int>, greater <int> > Q;
for (int i = 0; i < k; i++) {
Q.push(lst[i]);
}
for (int i = m + 1; i <= n + m; i++) {
int p = Q.top(); Q.pop();
f[i] = dec(add(f[i - 1], f[i - 1]), f[p - 1]);
Q.push(i);
}
printf("%d", f[n + m]);
return 0;
}