比较朴素的想法就是枚举子串的长度$l$并找到最大的$k$,这样做是$O(n^3)$的
我们可以用后缀数组优化这个过程,同样是枚举$l$,只不过我们可以把所有后缀按$height\\geq l$的连续段分组,先扫一遍找出所有长度$\\geq l$的出现多次的子串,然后按$sa$排序,贪心地从前往后取,看最大能取到的$k$是什么,就做完了
快排估计会超时,所以这里我们可以用计数排序做到$O(n^2)$的总时间复杂度
p.s.说实话一开始被$n=10^4$吓到了,但实际上这个平方不是满的,又有$\\text{2s}$时限,所以不虚==
#include<stdio.h> #include<string.h> struct pr{ int c[2],id; pr(int a=0,int b=0,int d=0){c[0]=a;c[1]=b;id=d;} }p[16010],q[16010]; bool operator!=(pr a,pr b){return a.c[0]!=b.c[0]||a.c[1]!=b.c[1];} int rk[32010],sa[16010],h[16010],c[16010],n; int max(int a,int b){return a>b?a:b;} void sort(int n,int f){ int i,m; memset(c,0,sizeof(c)); m=0; for(i=1;i<=n;i++){ m=max(m,p[i].c[f]); c[p[i].c[f]]++; } for(i=1;i<=m;i++)c[i]+=c[i-1]; for(i=n;i>0;i--)q[c[p[i].c[f]]--]=p[i]; for(i=1;i<=n;i++)p[i]=q[i]; } char s[16010]; void suf(){ int i,l,m; for(i=1;i<=n;i++)rk[i]=s[i]; for(l=1;l<=n;l<<=1){ for(i=1;i<=n;i++)p[i]=pr(rk[i],rk[i+l],i); sort(n,1); sort(n,0); m=0; for(i=1;i<=n;i++){ if(p[i]!=p[i-1])m++; rk[p[i].id]=m; } } for(i=1;i<=n;i++)sa[rk[i]]=i; l=0; for(i=1;i<=n;i++){ if(l)l--; while(s[i+l]==s[sa[rk[i]-1]+l])l++; h[rk[i]]=l; } } int main(){ int las,i,l,k,L,K,M,ans; scanf("%s",s+1); n=strlen(s+1); suf(); L=K=ans=0; for(l=1;l<=n/2;l++){ memset(c,0,sizeof(c)); M=1; for(i=2;i<=n;i++){ if(h[i]>=l) c[sa[i-1]]=c[sa[i]]=M; else M++; } M=0; for(i=1;i<=n;i++){ if(c[i]){ M++; p[M]=pr(c[i],i); } } sort(M,1); sort(M,0); las=p[1].c[1]; k=1; p[M+1].c[0]=0; for(i=2;i<=M+1;i++){ if(p[i].c[0]!=p[i-1].c[0]){ if(k*l>K*L){ K=k; L=l; ans=las; } k=1; las=p[i].c[1]; }else if(las+l<=p[i].c[1]){ k++; las=p[i].c[1]; } } } if(ans==0){ puts("-1"); return 0; } printf("%d\\n",L*K); for(i=ans;i<ans+L;i++)putchar(s[i]); }