网址:https://www.luogu.org/problem/P3435
题意:
一个串是有限个小写字符的序列,特别的,一个空序列也可以是一个串。 一个串$P$是串$A$的前缀, 当且仅当存在串$B$,使得 $A = PB$. 并且$P$不是一个空串,那么我们说$P$是$A$的一个$proper$前缀。 定义$Q$是$A$的周期, 当且仅当$Q$是$A$的一个$proper$前缀并且$A$是$QQ$的前缀(不一定要是$proper$前缀)。 比如串 $abab$ 和 $ababab$ 都是串 $abababa$ 的周期。串A的最大周期就是它最长的一个周期或者是一个空串(当$A$没有周期的时候)。比如说, $ababab$ 的最大周期是 $abab$。 串 $abc$ 的最大周期是空串。给出一个串,求出它所有前缀的最大周期长度之和。
说实话我也没有看懂,然后就试着自己理解了一下,$proper$前缀就是一个不为空串的前缀。$Q$是$A$的一个$proper$前缀且$A$是$QQ$的前缀,也就是$Q$的两倍能够覆盖$A$。对于给出的字符串,将其每个前缀设为$A$,求最大长度的$Q$,输出它们的和。
题解:
使用$KMP$算法:实际上是求每个前缀的最长前缀,所以处理完一次$next$数组后,对于$next$数组的除$next[0]$的每个值。设为$next[i]$,从前往后遍历$next$数组,如果$next[next[i]]$为$0$,则说明该前缀的最大前缀长度为$0$,不用处理。否则改为$next[next[i]]$,然后累加$i-next[i]$,此处如何理解呢?假设求得$next[next[i]]=a$,且$next[i]$没有被修改过,$a$就是以长度为$i$的字符串的最长前缀后缀相同长度的子串的最长前后缀相同长度。修改为$next[i]$,当后面有一个$j$,$next[j]=i$时,即$next[next[j]]=a$时,即原来的长度为$i$的子串是长度为$j$的子串的最长前缀,相当于增加了一个周期,此时自然是修改为$a$,即先找到最简单的串,即如果找到了$aba$,$next[3]=1$,后面扩展到$ababa$时增加了一个$ab$,即最小周期,此时不改变,以维持最长长度。$next[0]=-1$不需要处理。
AC代码:
#include <iostream> #include <string> using namespace std; int nextarray[1000005]; void getnext(string &str) { nextarray[0] = -1; int j = -1, k = 0; while (k < str.size()) { if (j == -1 || str[j] == str[k]) nextarray[++k] = ++j; else j = nextarray[j]; } } int main() { ios::sync_with_stdio(0); cin.tie(0); int n; cin >> n; string str; cin >> str; getnext(str); long long ans = 0; for (int i = 1; i <= n; ++i) if (nextarray[nextarray[i]] > 0)//有前缀的前缀 nextarray[i] = nextarray[nextarray[i]]; for (int i = 1; i <= n; ++i) if (nextarray[i]) ans += i - nextarray[i]; cout << ans << endl; return 0; }