http://www.spoj.com/problems/NSUBSTR/
题意:
F(x)定义为字符串S中所有长度为x的子串重复出现的最大次数
输出F[1]~F[len(S)]
用字符串S构建后缀自动机
若子串 str ∈状态s,那么子串str 在字符串S中出现的次数就是| Right(s) |
显然不能枚举所有状态的所有子串
但是我们可以线性的时间得到F[Max(s)]= | Right(s) |
然后再对F做一个后缀最大值即可
如何得到 一个状态Right集合的大小?
一个状态s的Right集合就是Parent树上,s的子树中叶子节点Right集合的并集
所以可以 从parent树上 底层向上层更新,即将底层节点的Right并入父节点的Right
这个可以通过拓扑排序实现
#include<cstdio> #include<cstring> #define N 250002 #define max(x,y) ((x)>(y) ? (x) : (y)) char s[N]; int tot=1,fa[N<<1],len[N<<1],ch[N<<1][26]; int last=1,p,np,q,nq; int front[N<<1],nxt[N<<1],to[N<<1],cnt; int r[N<<1],f[N]; int v[N<<1],sa[N<<1]; void extend(int c) { len[np=++tot]=len[last]+1; for(p=last;p && !ch[p][c]; p=fa[p]) ch[p][c]=np; if(!p) fa[np]=1; else { q=ch[p][c]; if(len[q]==len[p]+1) fa[np]=q; else { len[nq=++tot]=len[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[nq])); fa[nq]=fa[q]; fa[q]=fa[np]=nq; for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; } } last=np; } int main() { scanf("%s",s+1); int n=strlen(s+1); for(int i=1;i<=n;++i) { r[tot+1]=1; extend(s[i]-'a'); } for(int i=1;i<=tot;++i) v[len[i]]++; for(int i=1;i<=n;++i) v[i]+=v[i-1]; for(int i=1;i<=tot;++i) sa[v[len[i]]--]=i; for(int i=tot;i;--i) r[fa[sa[i]]]+=r[sa[i]]; for(int i=1;i<=tot;++i) f[len[i]]=max(f[len[i]],r[i]); for(int i=n-1;i;--i) f[i]=max(f[i],f[i+1]); for(int i=1;i<=n;++i) printf("%d ",f[i]); }