蓝书2.4 AC自动机
T1 玄武密码 bzoj 4327
题目大意:
一些字符串 求这些字符串的前缀在母串上的最大匹配长度是多少
思路:
对于所有串建立AC自动机
拿母串在自动机上匹配 对所有点打标记 以及对他们的fail打标记
查询每个串标记最长到哪即可
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstdlib> 5 #include<cstring> 6 #include<algorithm> 7 #include<vector> 8 #include<queue> 9 #define inf 2139062143 10 #define ll long long 11 #define MAXN 10100100 12 using namespace std; 13 inline int read() 14 { 15 int x=0,f=1;char ch=getchar(); 16 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 17 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 18 return x*f; 19 } 20 struct node{int fail,ch[4];}tr[MAXN]; 21 int n,m,vis[MAXN],tot; 22 char s[MAXN],ch[100100][110]; 23 int hsh(char c) 24 { 25 if(c=='E') return 0; 26 if(c=='S') return 1; 27 if(c=='W') return 2; 28 return 3; 29 } 30 void ins(char *c,int len) 31 { 32 int pos=0; 33 for(int i=0,k;i<len;i++) 34 { 35 k=hsh(c[i]); 36 if(!tr[pos].ch[k]) tr[pos].ch[k]=++tot; 37 pos=tr[pos].ch[k]; 38 } 39 } 40 int q[MAXN],l=1,r=0,x; 41 void build() 42 { 43 for(int i=0;i<4;i++) if(tr[0].ch[i]) q[++r]=tr[0].ch[i]; 44 while(l<=r) 45 { 46 x=q[l++]; 47 for(int i=0;i<4;i++) 48 if(tr[x].ch[i]) tr[tr[x].ch[i]].fail=tr[tr[x].fail].ch[i],q[++r]=tr[x].ch[i]; 49 else tr[x].ch[i]=tr[tr[x].fail].ch[i]; 50 } 51 } 52 void calc() 53 { 54 int pos=0,k,x; 55 for(int i=0;i<n;i++) 56 { 57 k=hsh(s[i]); 58 pos=tr[pos].ch[k],vis[pos]=1,x=pos; 59 while(tr[x].fail) vis[x=tr[x].fail]=1; 60 } 61 } 62 int query(char *c,int len) 63 { 64 int pos=0,k,res=0; 65 for(int i=0;i<len;i++) 66 { 67 k=hsh(c[i]); 68 pos=tr[pos].ch[k]; 69 if(vis[pos]) res=max(res,i+1); 70 else break; 71 } 72 return res; 73 } 74 int main() 75 { 76 n=read(),m=read(); 77 scanf("%s",s); 78 for(int i=1;i<=m;i++) 79 {scanf("%s",ch[i]);ins(ch[i],strlen(ch[i]));} 80 build();calc(); 81 for(int i=1;i<=m;i++) printf("%d\n",query(ch[i],strlen(ch[i]))); 82 }
T2 Censoring bzoj 3940
题目大意:
有个串S和一些单词 这些单词都不是其他的子串
每次在S中找到最早出现的列表中的单词,然后从S中删除这个单词
重复这个操作直到S中没有列表里的单词为止 输出最后的S
思路:
对于单词建立自动机
因为单词之间没有包含关系 所以可以暴力匹配
在匹配串S时 用手打栈模拟 如果匹配到end就减去这个单词 pos变为之前的pos
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstdlib> 5 #include<cstring> 6 #include<algorithm> 7 #include<vector> 8 #include<queue> 9 #define inf 2139062143 10 #define ll long long 11 #define MAXN 100100 12 using namespace std; 13 inline int read() 14 { 15 int x=0,f=1;char ch=getchar(); 16 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 17 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 18 return x*f; 19 } 20 struct node{int fail,ch[26];}tr[MAXN]; 21 int n,m,vis[MAXN],tot,ed[MAXN],top,p[MAXN]; 22 char s[MAXN],ch[MAXN],st[MAXN]; 23 void ins(char *c,int len) 24 { 25 int pos=0; 26 for(int i=0,k;i<len;i++) 27 { 28 k=c[i]-'a'; 29 if(!tr[pos].ch[k]) tr[pos].ch[k]=++tot; 30 pos=tr[pos].ch[k]; 31 } 32 ed[pos]=len; 33 } 34 int q[MAXN],l=1,r=0,x; 35 void build() 36 { 37 for(int i=0;i<26;i++) if(tr[0].ch[i]) q[++r]=tr[0].ch[i]; 38 while(l<=r) 39 { 40 x=q[l++]; 41 for(int i=0;i<26;i++) 42 if(tr[x].ch[i]) tr[tr[x].ch[i]].fail=tr[tr[x].fail].ch[i],q[++r]=tr[x].ch[i]; 43 else tr[x].ch[i]=tr[tr[x].fail].ch[i]; 44 } 45 } 46 int main() 47 { 48 scanf("%s",s);n=strlen(s),m=read(); 49 while(m--) {scanf("%s",ch);ins(ch,strlen(ch));} 50 build(); 51 for(int i=0,pos=0;i<n;i++) 52 { 53 st[++top]=s[i]; 54 pos=tr[pos].ch[s[i]-'a'],p[top]=pos; 55 if(ed[pos]) top-=ed[pos],pos=p[top]; 56 } 57 for(int i=1;i<=top;i++) printf("%c",st[i]); 58 }
T3 单词 bzoj 3172
题目大意:
一篇论文是由许多单词组成 求每个单词分别在论文中出现多少次 (论文为所有单词加#拼接起来)
思路:
建立ac自动机 可以想到如果一个单词出现那么它的fail也一定出现一次
每次加入一个单词对经过的节点加一 把每个节点的值加入它所有fail的值
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstdlib> 5 #include<cstring> 6 #include<algorithm> 7 #include<vector> 8 #include<queue> 9 #define inf 2139062143 10 #define ll long long 11 #define MAXN 1001000 12 using namespace std; 13 inline int read() 14 { 15 int x=0,f=1;char ch=getchar(); 16 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 17 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 18 return x*f; 19 } 20 struct node{int fail,ch[26];}tr[MAXN]; 21 int n,m,tot,ed[MAXN],g[MAXN],val[MAXN]; 22 char ch[MAXN]; 23 void ins(char *c,int len,int x) 24 { 25 int pos=0; 26 for(int i=0,k;i<len;i++) 27 { 28 k=c[i]-'a'; 29 if(!tr[pos].ch[k]) tr[pos].ch[k]=++tot; 30 pos=tr[pos].ch[k],val[pos]++; 31 } 32 g[x]=pos; 33 } 34 int q[MAXN],l=1,r=0,x; 35 void build() 36 { 37 for(int i=0;i<26;i++) if(tr[0].ch[i]) q[++r]=tr[0].ch[i]; 38 while(l<=r) 39 { 40 x=q[l++]; 41 for(int i=0;i<26;i++) 42 if(tr[x].ch[i]) tr[tr[x].ch[i]].fail=tr[tr[x].fail].ch[i],q[++r]=tr[x].ch[i]; 43 else tr[x].ch[i]=tr[tr[x].fail].ch[i]; 44 } 45 } 46 int main() 47 { 48 m=read(); 49 for(int i=1;i<=m;i++) {scanf("%s",ch);ins(ch,strlen(ch),i);} 50 build(); 51 for(int i=tot;i>=0;i--) val[tr[q[i]].fail]+=val[q[i]]; 52 for(int i=1;i<=m;i++) printf("%d\n",val[g[i]]); 53 }
T4 最短母串 bzoj 1195
题目大意:
n个字符串,要求找到一个最短的字符串T,使得这n个字符串都是T的子串
思路:
① 使用AC自动机 对每个end像状压一样标记 传递到fail上
从a到z bfs 如果bfs到状态访问过所有串 就结束
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstdlib> 5 #include<cstring> 6 #include<algorithm> 7 #include<vector> 8 #include<queue> 9 #define inf 2139062143 10 #define ll long long 11 #define MAXN 610*(1<<12) 12 using namespace std; 13 inline int read() 14 { 15 int x=0,f=1;char ch=getchar(); 16 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 17 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 18 return x*f; 19 } 20 struct node{int fail,ch[26];}tr[610]; 21 int n,m,tot,ed[610],t; 22 char ch[50]; 23 void ins(char *c,int len,int x) 24 { 25 int pos=0; 26 for(int i=0,k;i<len;i++) 27 { 28 k=c[i]-'A'; 29 if(!tr[pos].ch[k]) tr[pos].ch[k]=++tot; 30 pos=tr[pos].ch[k]; 31 } 32 ed[pos]|=(1<<x); 33 } 34 int q[610],l=1,r=0,x; 35 void build() 36 { 37 for(int i=0;i<26;i++) if(tr[0].ch[i]) q[++r]=tr[0].ch[i]; 38 while(l<=r) 39 { 40 x=q[l++]; 41 for(int i=0;i<26;i++) 42 if(tr[x].ch[i]) 43 tr[tr[x].ch[i]].fail=tr[tr[x].fail].ch[i],ed[tr[x].ch[i]]|=ed[tr[tr[x].fail].ch[i]],q[++r]=tr[x].ch[i]; 44 else tr[x].ch[i]=tr[tr[x].fail].ch[i]; 45 } 46 } 47 struct data {int pos,val;}g[MAXN]; 48 queue <data> que; 49 int ans[610],res,hd,tl,vis[610][1<<12]; 50 void bfs() 51 { 52 t=(1<<n)-1,hd=tl=1;que.push((data){0,0}); 53 while(hd<=tl) 54 { 55 int p=que.front().pos,st=que.front().val;que.pop(); 56 if(st==t) 57 { 58 while(hd>1) ans[++res]=g[hd].val,hd=g[hd].pos; 59 for(int i=res;i;i--) printf("%c",ans[i]+'A'); 60 return ; 61 } 62 for(int i=0;i<26;i++) 63 if(!vis[tr[p].ch[i]][st|ed[tr[p].ch[i]]]) 64 { 65 g[++tl]=(data){hd,i}; 66 que.push((data){tr[p].ch[i],(st|ed[tr[p].ch[i]])}); 67 vis[tr[p].ch[i]][st|ed[tr[p].ch[i]]]=1; 68 } 69 hd++; 70 } 71 } 72 int main() 73 { 74 n=read(); 75 for(int i=0;i<n;i++) {scanf("%s",ch);ins(ch,strlen(ch),i);} 76 build();bfs(); 77 }
② 状压 dp i j 表示 已经加入字符的状态为i j结尾的最小长度 同时开一个数组记录这个串 方便字符串比较
转移的时候枚举一下不在状态里的字符即可
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstdlib> 5 #include<cstring> 6 #include<algorithm> 7 #include<vector> 8 #include<queue> 9 #define inf 2139062143 10 #define ll long long 11 using namespace std; 12 inline int read() 13 { 14 int x=0,f=1;char ch=getchar(); 15 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 16 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 17 return x*f; 18 } 19 int n,k,dp[1<<12][12],add[12][12],ban[12],t,res=inf,kd; 20 char ch[15][55],ans[1<<12][12][610],tmp[610]; 21 int calc(char *y,char *x) 22 { 23 int l=strlen(y),t=min(strlen(x),strlen(y)); 24 for(int j,i=t;i;i--) 25 { 26 for(j=0;j<i;j++) 27 if(x[j]!=y[l-i+j]) break; 28 if(j==i) return i; 29 } 30 return 0; 31 } 32 void mdf(char *x,char *y,int k) 33 { 34 int lx=strlen(x),ly=strlen(y); 35 for(int i=0;i<lx;i++) tmp[i]=x[i]; 36 for(int i=k;i<ly;i++) tmp[lx+i-k]=y[i]; 37 } 38 int cmp(char *x,char *y) 39 { 40 int l=strlen(x); 41 for(int i=0;i<l;i++) 42 if(x[i]<y[i]) return 1; 43 else if(x[i]>y[i]) return 0; 44 return 1; 45 } 46 int main() 47 { 48 n=read();memset(dp,63,sizeof(dp)); 49 for(int i=0;i<n;i++) scanf("%s",ch[i]); 50 for(int i=0;i<n;i++) 51 for(int j=0;j<n;j++) 52 { 53 if(i==j) continue; 54 memset(tmp,0,sizeof(tmp)); 55 for(int k=0,l=0;k<strlen(ch[i]);k++) 56 { 57 tmp[l++]=ch[i][k]; 58 if(calc(tmp,ch[j])==strlen(ch[j])) ban[j]=1; 59 } 60 } 61 for(int i=0;i<n;i++) if(!ban[i]) 62 { 63 if(i==k) {k++;continue;} 64 memset(ch[k],0,sizeof(ch[k])); 65 for(int j=0,l=strlen(ch[i]);j<l;j++) ch[k][j]=ch[i][j]; 66 k++; 67 } 68 n=max(1,k),t=(1<<n)-1; 69 for(int i=0;i<n;i++) 70 for(int j=0;j<n;j++) 71 if(i!=j) add[i][j]=calc(ch[i],ch[j]); 72 for(int i=0;i<n;i++) 73 { 74 dp[1<<i][i]=strlen(ch[i]); 75 for(int j=0,l=strlen(ch[i]);j<l;j++) ans[1<<i][i][j]=ch[i][j]; 76 } 77 for(int i=1;i<t;i++) 78 for(int j=0;j<n;j++) 79 if(((1<<j)&i)==0) 80 { 81 for(int k=0;k<n;k++) 82 if((1<<k)&i) 83 if(dp[i|(1<<j)][j]==dp[i][k]+strlen(ch[j])-add[k][j]) 84 { 85 mdf(ans[i][k],ch[j],add[k][j]); 86 if(!cmp(ans[i|(1<<j)][j],tmp)) 87 { 88 memset(ans[i|(1<<j)][j],0,sizeof(ans[i|(1<<j)][j])); 89 for(int l=0;l<dp[i|(1<<j)][j];l++) 90 ans[i|(1<<j)][j][l]=tmp[l]; 91 } 92 } 93 else if(dp[i|(1<<j)][j]>dp[i][k]+strlen(ch[j])-add[k][j]) 94 { 95 dp[i|(1<<j)][j]=dp[i][k]+strlen(ch[j])-add[k][j]; 96 mdf(ans[i][k],ch[j],add[k][j]); 97 memset(ans[i|(1<<j)][j],0,sizeof(ans[i|(1<<j)][j])); 98 for(int l=0;l<dp[i|(1<<j)][j];l++) 99 ans[i|(1<<j)][j][l]=tmp[l]; 100 } 101 if(i|(1<<j)==t&&res==dp[t][j]&&cmp(ans[t][j],ans[t][kd])) kd=j; 102 if(i|(1<<j)==t&&res>dp[t][j]) res=dp[t][j],kd=j; 103 } 104 printf("%s",ans[t][kd]); 105 }
T5 病毒 bzoj 2938
题目大意:
询问是否有一个无限长的01串满足任意一个给出的串都不是它的自串
思路:
把end的标记传递 dfs找环 如果有环就说明可以找到一个满足题意的串
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstdlib> 5 #include<cstring> 6 #include<algorithm> 7 #include<vector> 8 #include<queue> 9 #define inf 2139062143 10 #define ll long long 11 #define MAXN 30100 12 using namespace std; 13 inline int read() 14 { 15 int x=0,f=1;char ch=getchar(); 16 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 17 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 18 return x*f; 19 } 20 struct node{int fail,ch[2];}tr[MAXN]; 21 int n,m,tot,ed[MAXN],t,vis[MAXN],tag[MAXN]; 22 char ch[MAXN]; 23 void ins(char *c,int len) 24 { 25 int pos=0; 26 for(int i=0,k;i<len;i++) 27 { 28 k=c[i]-'0'; 29 if(!tr[pos].ch[k]) tr[pos].ch[k]=++tot; 30 pos=tr[pos].ch[k]; 31 } 32 ed[pos]=1; 33 } 34 int q[MAXN],l=1,r=0,x; 35 void build() 36 { 37 for(int i=0;i<2;i++) if(tr[0].ch[i]) q[++r]=tr[0].ch[i]; 38 while(l<=r) 39 { 40 x=q[l++]; 41 for(int i=0;i<2;i++) 42 if(tr[x].ch[i]) 43 tr[tr[x].ch[i]].fail=tr[tr[x].fail].ch[i],ed[tr[x].ch[i]]|=ed[tr[tr[x].fail].ch[i]],q[++r]=tr[x].ch[i]; 44 else tr[x].ch[i]=tr[tr[x].fail].ch[i]; 45 } 46 } 47 int dfs(int x) 48 { 49 vis[x]=1; 50 for(int i=0;i<2;i++) 51 { 52 if(vis[tr[x].ch[i]]) return 1; 53 if(tag[tr[x].ch[i]]||ed[tr[x].ch[i]]) continue; 54 tag[tr[x].ch[i]]=1; 55 if(dfs(tr[x].ch[i])) return 1; 56 } 57 vis[x]=0;return 0; 58 } 59 int main() 60 { 61 n=read(); 62 for(int i=0;i<n;i++) {scanf("%s",ch);ins(ch,strlen(ch));} 63 build();puts(dfs(0)?"TAK":"NIE"); 64 }
T6 文本生成器 bzoj 1030