后缀自动机
理解起来好困难啊QAQ
WIKIOI3160 求两个串的最长公共子串
见CLJppt
1 char s[maxn]; 2 struct sam 3 { 4 int n,last,cnt; 5 int go[maxn][26],l[maxn],fa[maxn]; 6 void add(int x) 7 { 8 int p=last,np=last=++cnt;l[np]=l[p]+1; 9 for(;p&&!go[p][x];p=fa[p])go[p][x]=np; 10 if(!p)fa[np]=1; 11 else 12 { 13 int q=go[p][x]; 14 if(l[p]+1==l[q])fa[np]=q; 15 else 16 { 17 int nq=++cnt;l[nq]=l[p]+1; 18 memcpy(go[nq],go[q],sizeof(go[q])); 19 fa[nq]=fa[q]; 20 fa[np]=fa[q]=nq; 21 for(;p&&go[p][x]==q;p=fa[p])go[p][x]=nq; 22 } 23 } 24 } 25 void init() 26 { 27 last=cnt=1; 28 scanf("%s",s);int m=strlen(s); 29 for0(i,m-1)add(s[i]-'a'); 30 } 31 void solve() 32 { 33 scanf("%s",s);int m=strlen(s),now=1,t=0,ans=0; 34 for0(i,m-1) 35 { 36 int x=s[i]-'a'; 37 if(go[now][x])t++,now=go[now][x]; 38 else 39 { 40 while(now&&!go[now][x])now=fa[now]; 41 if(!now)t=0,now=1; 42 else t=l[now]+1,now=go[now][x]; 43 } 44 ans=max(ans,t); 45 } 46 printf("%d\n",ans); 47 } 48 }T; 49 int main() 50 { 51 T.init(); 52 T.solve(); 53 return 0; 54 }
BZOJ2555: SubString
正解是SAM+LCT,但出题人显然没有卡暴力。。。写了个暴力结果跑了rank4 233
犯了一个sb错就是没有更新lastQAQ
1 char s[2*maxn]; 2 int mask; 3 struct sam 4 { 5 int last,cnt,fa[maxn],go[maxn][26],l[maxn],r[maxn]; 6 sam(){last=cnt=1;} 7 void add(int x) 8 { 9 int p=last,np=++cnt;last=np;l[np]=l[p]+1; 10 for(;p&&!go[p][x];p=fa[p])go[p][x]=np; 11 if(!p)fa[np]=1; 12 else 13 { 14 int q=go[p][x]; 15 if(l[p]+1==l[q])fa[np]=q; 16 else 17 { 18 int nq=++cnt;l[nq]=l[p]+1; 19 memcpy(go[nq],go[q],sizeof(go[q])); 20 r[nq]=r[q]; 21 fa[nq]=fa[q]; 22 fa[q]=fa[np]=nq; 23 for(;p&&go[p][x]==q;p=fa[p])go[p][x]=nq; 24 } 25 } 26 for(;np;np=fa[np])r[np]++; 27 } 28 void insert(char s[]) 29 { 30 int n=strlen(s),t=mask; 31 for0(i,n-1){mask=(mask*131+i)%n;swap(s[i],s[mask]);} 32 mask=t; 33 for0(i,n-1)add(s[i]-'A'); 34 } 35 void query(char s[]) 36 { 37 int n=strlen(s),now=1,t=mask; 38 for0(i,n-1){mask=(mask*131+i)%n;swap(s[i],s[mask]);} 39 for0(i,n-1)now=go[now][s[i]-'A']; 40 mask=t^r[now]; 41 printf("%d\n",r[now]); 42 } 43 }T; 44 int main() 45 { 46 freopen("input.txt","r",stdin); 47 freopen("output.txt","w",stdout); 48 int Q=read(); 49 scanf("%s",s);int n=strlen(s);for0(i,n-1)T.add(s[i]-'A'); 50 while(Q--) 51 { 52 scanf("%s",s); 53 if(s[0]=='A'){scanf("%s",s);T.insert(s);} 54 else {scanf("%s",s);T.query(s);} 55 } 56 return 0; 57 }
BZOJ3238: [Ahoi2013]差异
后缀自动机其实反过来添加形成的parent树就是后缀树啦。
写出后缀树DFS一遍就ok啦 妈妈我会写后缀树啦
1 struct sam 2 { 3 int cnt,last,fa[maxn],go[maxn][26],head[maxn],l[maxn],s[maxn],tot;ll ans; 4 char ch[maxn]; 5 struct edge{int go,next;}e[maxn]; 6 void add(int x) 7 { 8 int p=last,np=last=++cnt;l[np]=l[p]+1; 9 for(;p&&!go[p][x];p=fa[p])go[p][x]=np; 10 if(!p)fa[np]=1; 11 else 12 { 13 int q=go[p][x]; 14 if(l[p]+1==l[q])fa[np]=q; 15 else 16 { 17 int nq=++cnt;l[nq]=l[p]+1; 18 memcpy(go[nq],go[q],sizeof(go[q])); 19 fa[nq]=fa[q]; 20 fa[q]=fa[np]=nq; 21 for(;p&&go[p][x]==q;p=fa[p])go[p][x]=nq; 22 } 23 } 24 s[np]=1; 25 } 26 void init() 27 { 28 last=cnt=1; 29 scanf("%s",ch);int n=strlen(ch);ans=(ll)(n-1)*n*(n+1)/2; 30 for3(i,n-1,0)add(ch[i]-'a'); 31 } 32 void add(int x,int y) 33 { 34 e[++tot]=(edge){y,head[x]};head[x]=tot; 35 } 36 void dfs(int x) 37 { 38 for4(i,x) 39 { 40 dfs(y); 41 ans-=(ll)2*s[y]*s[x]*l[x]; 42 s[x]+=s[y]; 43 } 44 } 45 void work() 46 { 47 for1(i,cnt)add(fa[i],i); 48 dfs(1); 49 cout<<ans<<endl; 50 } 51 }T; 52 int main() 53 { 54 freopen("input.txt","r",stdin); 55 freopen("output.txt","w",stdout); 56 T.init(); 57 T.work(); 58 return 0; 59 }
BZOJ2946: [Poi2000]公共串
多个串的LCS。可以SA,也可以SAM。
我们对每个节点保留mx[i][j]表示到i节点j串最多匹配多长,每个串做的时候就取max,然后这个节点对答案的贡献是min(),最后ans取max。
1 int n; 2 struct sam 3 { 4 int last,cnt,l[maxn],fa[maxn],go[maxn][26],mx[maxn][5]; 5 char s[maxn]; 6 void add(int x) 7 { 8 int p=last,np=last=++cnt;l[np]=l[p]+1; 9 for(;p&&!go[p][x];p=fa[p])go[p][x]=np; 10 if(!p)fa[np]=1; 11 else 12 { 13 int q=go[p][x]; 14 if(l[p]+1==l[q])fa[np]=q; 15 else 16 { 17 int nq=++cnt;l[nq]=l[p]+1; 18 memcpy(go[nq],go[q],sizeof(go[q])); 19 fa[nq]=fa[q]; 20 fa[q]=fa[np]=nq; 21 for(;p&&go[p][x]==q;p=fa[p])go[p][x]=nq; 22 } 23 } 24 } 25 void insert() 26 { 27 last=cnt=1; 28 scanf("%s",s);int n=strlen(s); 29 for0(i,n-1)add(s[i]-'a'); 30 } 31 void work(int k) 32 { 33 scanf("%s",s);int n=strlen(s),now=1,t=0; 34 for0(i,n-1) 35 { 36 int x=s[i]-'a'; 37 if(go[now][x]){t++;now=go[now][x];} 38 else 39 { 40 while(now&&!go[now][x])now=fa[now]; 41 if(!now){t=0;now=1;} 42 else t=l[now]+1,now=go[now][x]; 43 } 44 for(int j=now;j;j=fa[j])mx[j][k]=max(mx[j][k],t); 45 } 46 } 47 void print() 48 { 49 int ans=0; 50 for1(i,cnt) 51 { 52 int t=l[i]; 53 for0(j,n-1)t=min(t,mx[i][j]); 54 ans=max(ans,t); 55 } 56 cout<<ans<<endl; 57 } 58 }T; 59 int main() 60 { 61 freopen("input.txt","r",stdin); 62 freopen("output.txt","w",stdout); 63 n=read(); 64 T.insert(); 65 for0(i,n-1)T.work(i); 66 T.print(); 67 return 0; 68 }
BZOJ3879: SvT
构建出后缀树来就和差异那题一样了。1A简直感人肺腑。
1 int n,m,a[maxn],id[maxn],v[maxn],top,sta[maxn],dep[maxn]; 2 ll ans; 3 struct sam 4 { 5 int last,cnt,l[maxn],fa[maxn],head[maxn],go[maxn][26],tot,ti,dfn[maxn],f[maxn][20]; 6 struct edge{int go,next;}e[maxn]; 7 char s[maxn]; 8 void add(int x,int y) 9 { 10 e[++tot]=(edge){y,head[x]};head[x]=tot; 11 } 12 int add(int x) 13 { 14 int p=last,np=last=++cnt;l[np]=l[p]+1; 15 for(;p&&!go[p][x];p=fa[p])go[p][x]=np; 16 if(!p)fa[np]=1; 17 else 18 { 19 int q=go[p][x]; 20 if(l[p]+1==l[q])fa[np]=q; 21 else 22 { 23 int nq=++cnt;l[nq]=l[p]+1; 24 memcpy(go[nq],go[q],sizeof(go[q])); 25 fa[nq]=fa[q]; 26 fa[q]=fa[np]=nq; 27 for(;p&&go[p][x]==q;p=fa[p])go[p][x]=nq; 28 } 29 } 30 return np; 31 } 32 int lca(int x,int y) 33 { 34 if(dep[x]<dep[y])swap(x,y); 35 int t=dep[x]-dep[y]; 36 for0(i,18)if(t>>i&1)x=f[x][i]; 37 if(x==y)return x; 38 for3(i,18,0)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; 39 return f[x][0]; 40 } 41 void dfs(int x) 42 { 43 dfn[x]=++ti; 44 for1(i,18)if(dep[x]>=1<<i)f[x][i]=f[f[x][i-1]][i-1];else break; 45 for4(i,x) 46 { 47 dep[y]=dep[x]+1;f[y][0]=x; 48 dfs(y); 49 } 50 } 51 void init() 52 { 53 last=cnt=1; 54 scanf("%s",s+1);int n=strlen(s+1); 55 for3(i,n,1)id[i]=add(s[i]-'a'); 56 for1(i,cnt)add(fa[i],i); 57 dfs(1); 58 } 59 }T; 60 struct graph 61 { 62 int head[maxn],tot,s[maxn]; 63 struct edge{int go,next;}e[maxn]; 64 void add(int x,int y) 65 { 66 e[++tot]=(edge){y,head[x]};head[x]=tot; 67 } 68 void dfs(int x) 69 { 70 s[x]=v[x]; 71 for4(i,x) 72 { 73 dfs(y); 74 ans+=(ll)s[x]*s[y]*T.l[x]; 75 s[x]+=s[y]; 76 } 77 head[x]=0; 78 } 79 }G; 80 inline bool cmp(int x,int y){return T.dfn[x]<T.dfn[y];} 81 int main() 82 { 83 freopen("input.txt","r",stdin); 84 freopen("output.txt","w",stdout); 85 n=read();m=read(); 86 T.init(); 87 while(m--) 88 { 89 int k=read(); 90 for1(i,k)a[i]=id[read()]; 91 sort(a+1,a+k+1,cmp); 92 for1(i,k)v[a[i]]=1; 93 sta[top=1]=1;G.tot=0; 94 for1(i,k) 95 { 96 int x=a[i],f=T.lca(sta[top],x); 97 while(dep[f]<dep[sta[top]]) 98 { 99 if(dep[f]>=dep[sta[top-1]]) 100 { 101 G.add(f,sta[top--]); 102 if(sta[top]!=f)sta[++top]=f; 103 break; 104 } 105 G.add(sta[top-1],sta[top]);top--; 106 } 107 if(sta[top]!=x)sta[++top]=x; 108 } 109 while(--top)G.add(sta[top],sta[top+1]); 110 ans=0; 111 G.dfs(1); 112 printf("%I64d\n",ans); 113 for1(i,k)v[a[i]]=0; 114 } 115 return 0; 116 }
2780: [Spoj]8093 Sevenk Love Oimaster
给出n个串,在给出m个询问,每次询问一个串s在给出的n个串中多少个串中出现了.
好像后缀数组也能做?跑出sa,然后二分出左右端点,再上一次 hill的项链?估计也不会好写到哪里去...(就是不知道能不能建出SA)
还是说SAM做法。
所谓广义后缀自动机,就是多个串的后缀自动机。具体构建方法为了不存在两个串首尾相接构成新的串的情况,每加入一个串,我们就让last=root。
然后往下走的时候如果已经有出边了{l[go[last][x]]==l[last]+1那么就直接last=go[last][x],否则新建一个节点balabala。}否则还按原来的来。
然后因为一个节点有可能属于不同的串。所以我们要在后面挂链表。
那么我们对每个询问串从root沿着出边走,结束的时候在parent树中它在子树中的任意一个串中都出现了。这样问题就成了多次询问一个区间内有多少个不同的数字了。
@SDOI2009HH的项链
第一次感觉自己的代码写的好丑TAT
1 struct graph 2 { 3 int tot,head[maxn]; 4 struct edge{int go,next;}e[maxn]; 5 void add(int x,int y) 6 { 7 e[++tot]=(edge){y,head[x]};head[x]=tot; 8 } 9 }A,B; 10 int s[maxn],n,m,pos[maxn]; 11 struct rec{int x,y,id;}a[maxn]; 12 struct sam 13 { 14 int cnt,last,l[maxn],id[maxn][2],fa[maxn],ti; 15 map<int,int>go[maxn]; 16 char s[maxn]; 17 sam(){cnt=1;} 18 void add(int x,int y) 19 { 20 int p=last,q; 21 if(q=go[p][x]) 22 { 23 if(l[p]+1==l[q])last=q; 24 else 25 { 26 int nq=++cnt;l[nq]=l[p]+1; 27 go[nq]=go[q]; 28 fa[nq]=fa[q]; 29 fa[q]=nq; 30 for(;p&&go[p][x]==q;p=fa[p])go[p][x]=nq; 31 last=nq; 32 } 33 }else 34 { 35 int np=++cnt;l[np]=l[p]+1; 36 for(;p&&!go[p][x];p=fa[p])go[p][x]=np; 37 if(!p)fa[np]=1; 38 else 39 { 40 q=go[p][x]; 41 if(l[p]+1==l[q])fa[np]=q; 42 else 43 { 44 int nq=++cnt;l[nq]=l[p]+1; 45 go[nq]=go[q]; 46 fa[nq]=fa[q]; 47 fa[q]=fa[np]=nq; 48 for(;p&&go[p][x]==q;p=fa[p])go[p][x]=nq; 49 } 50 } 51 last=np; 52 } 53 B.add(last,y);//printf("%d %d\n",last,y); 54 } 55 void dfs(int x) 56 { 57 id[x][0]=++ti;pos[ti]=x; 58 for4(A,i,x)dfs(y); 59 id[x][1]=ti; 60 } 61 void insert(int y) 62 { 63 last=1; 64 scanf("%s",s);int n=strlen(s); 65 for0(i,n-1)add(s[i],y); 66 } 67 void work() 68 { 69 for1(i,cnt)A.add(fa[i],i); 70 dfs(1); 71 } 72 rec find(int j) 73 { 74 scanf("%s",s);int n=strlen(s),now=1; 75 for0(i,n-1)now=go[now][s[i]]; 76 return now?(rec){id[now][0],id[now][1],j}:(rec){2,1,j}; 77 } 78 }T; 79 void update(int x,int y) 80 { 81 //printf("%d %d\n",x,y); 82 for(;x<=T.cnt;x+=x&(-x))s[x]+=y; 83 } 84 int sum(int x) 85 { 86 int t=0; 87 for(;x;x-=x&(-x))t+=s[x]; 88 //cout<<x<<' '<<t<<endl; 89 return t; 90 } 91 int v[maxn],ans[maxn]; 92 inline bool cmp(rec a,rec b){return a.y<b.y;} 93 int main() 94 { 95 freopen("input.txt","r",stdin); 96 freopen("output.txt","w",stdout); 97 n=read();m=read(); 98 for1(i,n)T.insert(i); 99 T.work(); 100 for1(i,m)a[i]=T.find(i); 101 sort(a+1,a+m+1,cmp); 102 //for1(i,m)cout<<i<<' '<<a[i].x<<' '<<a[i].y<<' '<<a[i].id<<endl; 103 int now=1; 104 for1(i,T.cnt) 105 { 106 for4(B,j,pos[i]) 107 { 108 if(v[y])update(v[y],-1); 109 v[y]=i; 110 update(v[y],1); 111 } 112 //cout<<i<<' '<<a[now].x<<' '<<a[now].y<<' '<<a[now].id<<endl; 113 while(a[now].y==i)ans[a[now].id]=sum(a[now].y)-sum(a[now].x-1),now++; 114 } 115 for1(i,m)printf("%d\n",ans[i]); 116 return 0; 117 }
3473: 字符串
给出n个串,问每个串有多少个子串在至少k个串中都出现了。
类似于上一题,我们可以用“项链”的方法离线求出所有节点所代表的字符串的集合在多少个字符串中出现过。
如果他的出现次数>=k次,那么他对答案的贡献是l[i]-l[fa[i]]然后一个后缀对答案的总贡献就是他到根节点的权值和。然后再预处理一下就可以了。
修改了一下代码感觉美观多了
1 int n,last,cnt=1,ti,k; 2 typedef int arr[maxn]; 3 arr l,fa,s,f,pos,a,v; 4 int go[maxn][26],id[maxn][2]; 5 char ch[maxn]; 6 struct graph 7 { 8 int tot,head[maxn]; 9 struct edge{int go,next;}e[maxn]; 10 inline void add(int x,int y) 11 { 12 e[++tot]=(edge){y,head[x]};head[x]=tot; 13 } 14 }A,B,C; 15 inline void add(int x) 16 { 17 int p=last,q; 18 if(q=go[p][x]) 19 { 20 if(l[p]+1==l[q])last=q; 21 else 22 { 23 int nq=++cnt;l[nq]=l[p]+1; 24 memcpy(go[nq],go[q],sizeof(go[q])); 25 fa[nq]=fa[q]; 26 fa[q]=nq; 27 for(;p&&go[p][x]==q;p=fa[p])go[p][x]=nq; 28 last=nq; 29 } 30 }else 31 { 32 int np=++cnt;l[np]=l[p]+1; 33 for(;p&&!go[p][x];p=fa[p])go[p][x]=np; 34 if(!p)fa[np]=1; 35 else 36 { 37 q=go[p][x]; 38 if(l[p]+1==l[q])fa[np]=q; 39 else 40 { 41 int nq=++cnt;l[nq]=l[p]+1; 42 memcpy(go[nq],go[q],sizeof(go[q])); 43 fa[nq]=fa[q]; 44 fa[q]=fa[np]=nq; 45 for(;p&&go[p][x]==q;p=fa[p])go[p][x]=nq; 46 } 47 } 48 last=np; 49 } 50 } 51 inline void dfs(int x) 52 { 53 id[x][0]=++ti;pos[ti]=x; 54 for4(C,i,x)dfs(y); 55 id[x][1]=ti; 56 } 57 inline void dfs2(int x) 58 { 59 for4(C,i,x)f[y]+=f[x],dfs2(y); 60 } 61 inline void update(int x,int y) 62 { 63 for(;x<=cnt;x+=x&(-x))s[x]+=y; 64 } 65 inline int sum(int x) 66 { 67 int t=0; 68 for(;x;x-=x&(-x))t+=s[x]; 69 return t; 70 } 71 inline bool cmp(int x,int y){return id[x][1]<id[y][1];} 72 int main() 73 { 74 freopen("input.txt","r",stdin); 75 freopen("output.txt","w",stdout); 76 n=read();k=read(); 77 for1(i,n) 78 { 79 last=1; 80 scanf("%s",ch);int m=strlen(ch); 81 for0(j,m-1)add(ch[j]-'a'),A.add(last,i),B.add(i,last); 82 } 83 for1(i,cnt)C.add(fa[i],i); 84 dfs(1); 85 for1(i,cnt)a[i]=i; 86 sort(a+1,a+cnt+1,cmp); 87 int now=1; 88 for1(i,cnt) 89 { 90 for4(A,j,pos[i]) 91 { 92 if(v[y])update(v[y],-1); 93 v[y]=i; 94 update(v[y],1); 95 } 96 while(id[a[now]][1]==i)f[a[now]]=(sum(id[a[now]][1])-sum(id[a[now]][0]-1))>=k?l[a[now]]-l[fa[a[now]]]:0,now++; 97 } 98 dfs2(1); 99 for1(i,n) 100 { 101 ll ans=0; 102 for4(B,j,i)ans+=f[y]; 103 printf("%lld",ans);if(i!=n)printf(" "); 104 } 105 return 0; 106 }
3926: [Zjoi2015]诸神眷顾的幻想乡
[捂脸熊]没想到这题这么简单233 因为叶子很少,所以直接枚举所有的叶子把从他开始dfs顺便建立建立广义自动机,然后求本质不同的字符串的个数。然后就完了。。。
1 int n,k,tot,cnt,last; 2 typedef int arr[maxn]; 3 arr fa,l,head,du,a; 4 int go[maxn][12]; 5 struct edge{int go,next;}e[maxn]; 6 inline void add(int x,int y) 7 { 8 e[++tot]=(edge){y,head[x]};head[x]=tot;du[x]++; 9 e[++tot]=(edge){x,head[y]};head[y]=tot;du[y]++; 10 } 11 inline void add(int x) 12 { 13 int p=last,q; 14 if(q=go[p][x]) 15 { 16 if(l[p]+1==l[q])last=q; 17 else 18 { 19 int nq=++cnt;l[nq]=l[p]+1; 20 memcpy(go[nq],go[q],sizeof(go[q])); 21 fa[nq]=fa[q]; 22 fa[q]=nq; 23 for(;p&&go[p][x]==q;p=fa[p])go[p][x]=nq; 24 last=nq; 25 } 26 }else 27 { 28 int np=++cnt;l[np]=l[p]+1; 29 for(;p&&!go[p][x];p=fa[p])go[p][x]=np; 30 if(!p)fa[np]=1; 31 else 32 { 33 q=go[p][x]; 34 if(l[p]+1==l[q])fa[np]=q; 35 else 36 { 37 int nq=++cnt;l[nq]=l[p]+1; 38 memcpy(go[nq],go[q],sizeof(go[q])); 39 fa[nq]=fa[q]; 40 fa[q]=fa[np]=nq; 41 for(;p&&go[p][x]==q;p=fa[p])go[p][x]=nq; 42 } 43 } 44 last=np; 45 } 46 } 47 inline void dfs(int x,int f,int z) 48 { 49 add(a[x]); 50 int t=last; 51 for4(i,x)if(y!=f){dfs(y,x,last);last=t;} 52 } 53 int main() 54 { 55 freopen("input.txt","r",stdin); 56 freopen("output.txt","w",stdout); 57 n=read();k=read(); 58 for1(i,n)a[i]=read(); 59 for1(i,n-1)add(read(),read()); 60 cnt=1; 61 for1(i,n)if(du[i]==1)dfs(i,0,last=1); 62 ll ans=0; 63 for1(i,cnt)ans+=l[i]-l[fa[i]]; 64 cout<<ans<<endl; 65 return 0; 66 }