SPOJ 8222 Substrings (后缀自动机)
转载:http://hzwer.com/4420.html
给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值。
求F(1)..F(Length(S)) Length(S) <= 250000
我们构造S的后缀自动机(SAM),那么对于一个节点s,它的长度范围是[Min(s),Max(s)],同时他的出现次数是|Right(s)|。
那么我们用 |Right(s)|去更新F(Max(s))的值。同时最后从大到小依次用F(i)去更新F(i-1)即可。
对于SAM,我口胡几句吧
自动机每个结点信息是一个right集合
比如串aaabbaaabd
aaab在其中1-4 6-9出现
则自动机在读入aaab到达的结点是{4,9}
aab和ab也应该到达这个结点,因为他们出现的结尾也是{4,9}
而b是{4,5,9}不到达这个结点
显然,对于一个状态s,若长度l<=r所对应的al,ar满足
ST(al)=ST(ar)=s那么对于任意的l<=x<=r,ST(ax)=s
不妨设s所有合法集合为[Min(s),Max(s)]
al指的是串a的前l个
显然节点信息是集合包含关系
随着串长增加就是集合元素减少
构建自动机的目标就是让所有包含关系变为一棵树
构建实际上很简单。。。
1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 17 17 18 18 19 19 20 20 21 21 22 22 23 23 24 24 25 25 26 26 27 27 28 28 29 29 30 30 31 31 32 32 33 33 34 34 35 35 36 36 37 37 38 38 39 39 40 40 41 41 42 42 43 43 44 44 45 45 46 46 47 47 48 48 49 49 50 50 51 51 52 52 53 53 54 #include<iostream> 55 #include<cstdio> 56 #include<cstring> 57 #include<cstdlib> 58 #include<algorithm> 59 #include<cmath> 60 #define N 500005 61 #define ll long long 62 using namespace std; 63 char s[N]; 64 int S,cnt,last; 65 int a[N],b[N],f[N],t[N],fa[N],l[N],r[N],ch[N][26]; 66 void add(int x) 67 { 68 int c=a[x]; 69 int p=last,np=++cnt;last=np; 70 l[np]=x; 71 for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np; 72 if(!p)fa[np]=S; 73 else 74 { 75 int q=ch[p][c]; 76 if(l[p]+1==l[q])fa[np]=q; 77 else 78 { 79 int nq=++cnt;l[nq]=l[p]+1; 80 memcpy(ch[nq],ch[q],sizeof ch[q]); 81 fa[nq]=fa[q]; 82 fa[np]=fa[q]=nq; 83 for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq; 84 } 85 } 86 } 87 int main() 88 { 89 scanf("%s",s+1); 90 last=S=++cnt; 91 int len=strlen(s+1); 92 for(int i=1;i<=len;i++)a[i]=s[i]-'a'; 93 for(int i=1;i<=len;i++)add(i); 94 for(int i=1,p=S;i<=len;i++) 95 { 96 p=ch[p][a[i]];r[p]++; 97 } 98 for(int i=1;i<=cnt;i++)b[l[i]]++; 99 for(int i=1;i<=len;i++)b[i]+=b[i-1]; 100 for(int i=1;i<=cnt;i++)t[b[l[i]]--]=i; 101 for(int i=cnt;i;i--)r[fa[t[i]]]+=r[t[i]]; 102 for(int i=1;i<=cnt;i++)f[l[i]]=max(f[l[i]],r[i]); 103 for(int i=len;i;i--)f[i]=max(f[i+1],f[i]); 104 for(int i=1;i<=len;i++)printf("%d\n",f[i]); 105 return 0; 106 }