题面
给你一个长度为n的长字符串,“完美子串”既是它的前缀也是它的后缀,求“完美子串”的个数且统计这些子串的在长字符串中出现的次数。
很自然地往 KMP 的方向想,根据 fail 数组可以很容易地算出完美字串的个数,但发现每个串出现次数只能 O ( N ) O(N) O(N) 处理,那么全部的最坏复杂度为 O ( N 2 ) O(N^2) O(N2)。
这时候很容易在考虑 KMP 上陷进去,如何处理每个前缀串的出现次数?注意到是前缀串,考虑扩展KMP。
完美子串依旧很好求, i + Z i − 1 = n i+Z_i-1=n i+Zi−1=n, Z i Z_i Zi 指 Z 函数。出现次数可以通过用桶存 Z i Z_i Zi 再求后缀和求得,结论很显然。
整题关键在于想到扩K求解。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
char s[N];
int n,tot,nt[N],t[N];
void ExKMP(){
nt[0]=n;
int p=0;
while (p+1<n&&s[p]==s[p+1]) p++;
nt[1]=p;
int k=1;
for(int i=2;i<n;i++){
p=k+nt[k]-1;
int l=nt[i-k];
if(i+l-1<p) nt[i]=l;
else{
int j=max(0,p-i+1);
while (i+j<n&&s[j]==s[i+j]) j++;
nt[i]=j;
k=i;
}
}return ;
}
int main(){
scanf("%s",s);
n=strlen(s);
ExKMP();
for(int i=0;i<n;i++) t[nt[i]]++;
for(int i=n-1;~i;i--) t[i]+=t[i+1];
for(int i=0;i<n;i++) if(i+nt[i]==n) tot++;
printf("%d\n",tot);
for(int i=n-1;~i;i--) if(i+nt[i]==n) printf("%d %d\n",nt[i],t[nt[i]]);
return 0;
}
/*
start coding:15:15
finish debuging:15:35
*/