先求SA,然后按字典序从小到大枚举子串,每到一个后缀从长到短枚举子串(跳过长为he[i]的和前一段重复的子串),然后维护一个点p,保证i~p之间最小的he>=当前枚举长度,p是单调向右移的
然后把每个后缀的结果倒着输出即可#include#include #include using namespace std;const int N=3005;int n,m,wa[N],wb[N],wv[N],wsu[N],sa[N],rk[N],he[N],a[N],tot;char s[N];bool cmp(int r[],int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l];}void saa(char r[],int n,int m){ int *x=wa,*y=wb; for(int i=0;i<=m;i++) wsu[i]=0; for(int i=1;i<=n;i++) wsu[x[i]=r[i]]++; for(int i=2;i<=m;i++) wsu[i]+=wsu[i-1]; for(int i=n;i>=1;i--) sa[wsu[x[i]]--]=i; for(int j=1,p=1;j<=n&&p <<=1,m=p) { p=0; for(int i=n-j+1;i<=n;i++) y[++p]=i; for(int i=1;i<=n;i++) if(sa[i]>j) y[++p]=sa[i]-j; for(int i=1;i<=n;i++) wv[i]=x[y[i]]; for(int i=0;i<=m;i++) wsu[i]=0; for(int i=1;i<=n;i++) wsu[wv[i]]++; for(int i=2;i<=m;i++) wsu[i]+=wsu[i-1]; for(int i=n;i>=1;i--) sa[wsu[wv[i]]--]=y[i]; swap(x,y); p=1; x[sa[1]]=1; for(int i=2;i<=n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p:++p; } for(int i=1;i<=n;i++) rk[sa[i]]=i; for(int i=1,j,k=0;i<=n;he[rk[i++]]=k) for(k?k--:0,j=sa[rk[i]-1];r[i+k]==r[j+k];k++);}int main(){ scanf("%d%s",&n,s+1); saa(s,n,200); // for(int i=1;i<=n;i++) // cerr< <<" "< < =he[i]+1;j--) { while(p =j) p++; a[++tot]=p-i+1; } for(int j=tot;j>=1;j--) if(a[j]>1) printf("%d\n",a[j]); } return 0;}