zoukankan      html  css  js  c++  java
  • 蓝书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
    ",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
    ",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

    题解链接

  • 相关阅读:
    【bzoj2006】超级钢琴
    【bzoj4940】这是我自己的发明
    【arc076E】Connected?
    【agc004C】AND Grid
    选举
    几何
    打击目标
    【CF Gym100228】Graph of Inversions
    【CodeChef】Chef and Graph Queries
    大包子玩游戏
  • 原文地址:https://www.cnblogs.com/yyc-jack-0920/p/9340445.html
Copyright © 2011-2022 走看看