蓝书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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

② 状压 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 }
View Code

 

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 }
View Code

 

T6 文本生成器 bzoj 1030

题解链接

posted @ 2018-07-21 17:43  jack_yyc  阅读(221)  评论(0编辑  收藏  举报