周期长度和
题目链接:ybt高效进阶2-3-3
题目大意
给你一个串,要你求每个前缀的最大周期长度之和。
一个串的周期就是它是它的一个子串,但是不能是空串也不不能这个串本身,而且这个子串复制一遍之后形成一个新的字符串,这个新的字符串要包含原来的串。
思路
其实就是要找一个子串,复制一遍之后包含了原来串。对于每个子串倒要找长度最大的那个。
那我们其实就是要让原来的串分成两份,然后前面的那个包含后面的那个。然后周期就是前面的那个。
那我们看看怎样搞,才能让前面的包含后面的那个。
我们跟 KMP 联想一下,发下它就是 KMP。
那我们要让周期长度最大,就是要让前面的尽可能大,后面的尽可能小。
那后面的长度其实就是 KMP 的长度,那我们要让这个长度尽可能小,那就是要 KMP 更小?
那其实是要找到能匹配的最小的。那就是不断得递归 f a i l i fail_i faili 到 0 0 0,然后到 f a i l i = 0 fail_i=0 faili=0 的时候, i i i 就是最小的。
当然我们可以用类似并查集的思想来进行记忆化,从而减少时间。
代码
#include<cstdio>
using namespace std;
int n, j, fail[1000001];
long long ans;
char c[1000001];
int find(int now) {
if (fail[now]) return fail[now] = find(fail[now]);
return now;
}
int main() {
scanf("%d", &n);
scanf("%s", c + 1);
j = 0;
for (int i = 2; i <= n; i++) {
//KMP
while (j && c[i] != c[j + 1]) j = fail[j];
if (c[i] == c[j + 1]) j++;
fail[i] = j;
}
for (int i = 1; i <= n; i++) {
ans += 1ll * i - 1ll * find(i);
}
printf("%lld", ans);
return 0;
}