一开始以为这道题是后缀数组,想了好久没想明白怎么做,后来发现暴力枚举起点,进行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;
}