http://uoj.ac/problem/5

题意:对于字符串S,求出每个前缀子串各有几个等于前缀且不相交的后缀。

就是要求在求解KMP的过程中能经过几次压缩,然后多了一个子串长度不能超过一半的要求。

但是显然不能对每个子串重新求一遍。可以发现KMP的next中每个串的后续是唯一的,也就是说再往后求就是重复计算了,所以考虑把算完的存下来,每次找到第一个符合条件的就直接用它的答案。

下面的程序里有两个指针j和k,要注意j不等于k,为了减少时间,重复利用了k,保证k一直都在串的前半截移动,虽然每次都从nxt[i]开始答案没错,但是会超时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e6 + 10;
const ll mod = 1e9 + 7;
int T;
char s[N];
int nxt[N], cnt[N];
int main() {
scanf("%d", &T);
while (T--) {
scanf("%s", s + 1);
int n = strlen(s + 1);
ll ans = 1ll;
memset(nxt, 0, sizeof(nxt));
memset(cnt, 0, sizeof(cnt));
cnt[1] = 1;
for (int i = 2, j = 0, k = 0; i <= n; i++) {
while (j&&s[i] != s[j + 1])j = nxt[j];
if (s[i] == s[j + 1])j++;
nxt[i] = j;
cnt[i] = cnt[j] + 1;
while (k&&s[i] != s[k + 1])k = nxt[k];
if (s[i] == s[k + 1])k++;
while ((k << 1) > i)k = nxt[k];
ans = ans * (cnt[k] + 1ll) % mod;
}
printf("%lld\n", ans);
}
return 0;
}