一开始以为这道题是后缀数组,想了好久没想明白怎么做,后来发现暴力枚举起点,进行KMP就好了。
枚举起点以后,构建fail数组。
遍历fail数组,如果串s[l,r]是由串s[s,t]重复k次得到的,那么fail数组的样子一定如下
index: [l] [l+1] [l+2] ....[t] [t+1] [t+2]....[r]
fail: [0] [*] [*] ...[*] [1] [2] [3] ....[r-t]
并且(r-l+1)一定是(t-s+1)的倍数。
len = r-l+1
如果len > ans[k],记把ans数组k的位置更新成len。
这样的话我们在输出答案的时候,要输出重复t次的最长串长度,那么答案一定是ans[t],ans[2t]...ans[kt]里面的最大值
因为重复2t次也可以看作是重复t次,相邻的2个合成1个来看。
时间复杂度
O(n*n+n+nlogn)=O(n2)
#include <bits/stdc++.h>
using namespace std;
#define MAXN 2000001
const int maxn = 1005;
char str[maxn];
char *s;
int fail[maxn];
int ans[maxn];
void make_fail()
{
for (int i = 1, j = 0; s[i]; i++){
while (j && s[i] != s[j])j = fail[j - 1];
if (s[i] == s[j])fail[i] = ++j;
else fail[i] = 0;
}
}
int cas = 0;
void solve(){
memset(ans,0,sizeof(ans));
int len = strlen(str);
int pre = -1;
for(int i = 0;i < len;i++){
s = str+i;
make_fail();
for(int j = 0;s[j];j++){
if(fail[j] == pre + 1){
pre++;
if(pre >= 1 && (j+1)%(j+1-pre) == 0){
ans[(j+1)/(j+1-pre)] = max(ans[(j+1)/(j+1-pre)],j+1);
}
}
else if(fail[j] < pre){
pre = 0;
}
}
}
/*
for(int i = 0;i < len;++i){
cout<<ans[i]<<endl;
}
*/
printf("Case #%d: %d",++cas,len);
for(int i = 2;i <= len;i++){
int pt = 0;
for(int j = i;j <= len;j += i){
pt = max(pt,ans[j]);
}
printf(" %d",pt);
}
puts("");
}
int main(){
int T = 1;
cin>>T;
while(T--){
cin>>str;
solve();
}
return 0;
}