字符串(3)AC自动机

AC自动机真神奇,其实说白了就是在trie树上进行kmp模式匹配,不过刚接触确实有些难度,有些思想确实有些难以理解,所以学习的时候最好亲自手动模拟整个算法的全过程,那我就来写篇blog总结一下。

首先我们需要明白AC自动机是用来干什么的,首先我们知道kmp算法是用来解决单模式串匹配问题的,那么如果模式串不止一个,我们该怎么办呢?没错,AC自动机。我们可以把所有的模式串建立一棵字典树,然后在字典树上进行自我匹配建立next数组,最后利用next数组与主串进行匹配。

建立trie树没有什么问题,最难的地方估计是建立next数组的过程,那我就来手动模拟一下。

假设模式串为:AAAA  ABA  BBA BBB

主串为:AAAABBABABABB

首先我们建立字典树:

不过AC自动机里的字典树和普通的trie有所不同,这里的trie定义了一个0号虚结点,并且0号结点的所有出边都连向一号点,也就是说我们可以理解为1号结点代表的所有的字符集,然后我们将一号点的next指向0号点。

对于2号结点,我们有f[2]=1(其中f[i]表示i结点的父结点)那么我们就看一下1号点的next指向的0号点是否含有A这个儿子。显然1号结点就是这样的结点,所以2号点的next连向1。

同样的我们对于三号点也进行同样的操作,由于一号点的next是0,而0有B这样的儿子,所以把3的next连向1。

对于其余的结点我们也进行一样的操作:

但是对于8号点,它的父亲是5号点,5号点的next为3号点,然而三号点没有A这个儿子结点,那我们就继续查询3号点的next 1号点,一号点有A这个儿子,所以把8号点的next指向2号点。

然后我们就可以建立整棵trie树的next数组了。

这里有一个问题,我们在询问8号点时,重复跳了几次next这样便使得时间复杂度超过我们期望的O(n),所以我们需要进行一些神奇的操作。

我们在询问三号结点A这个儿子的时候,由于它不存在,一般情况我们就会continue,然后继续询问他的其它儿子,但是我们在询问9号点时,再一次访问了不存在的3号点的A这个儿子,而我们又会继续访问3号点的next所指的结点的A这个儿子,也就是说3号点的A这个儿子在整个操作中完全没有作用但是我们还会重复访问,所以我们就直接把3号点的A儿子直接定义为2号点,也就是next[3]:1的A儿子。这样我们在询问8号点的时候就可以直接将next[9]赋值为9的父结点的next结点的A这个儿子,也就是2号点。这样我们就是实现了O(n)的复杂度来建立next数组。(刚刚接触可能不是很理解,自己多画图模拟就明白了)

建立起next数组后,我们就可以直接让主串在trie树上跑,然后就可以愉快的dp了。

 

 1 void trie(char *s)
 2 {
 3     int len=strlen(s),u=1;
 4     for(int i=0;i<len;i++)
 5     {
 6         int c=s[i]-'a';
 7         if(!tree[u][c])
 8         {
 9             tree[u][c]=++tot;
10         }
11         u=tree[u][c];
12     }
13     bo[u]++;//记录每个模式串结尾的位置 
14 }
建立trie树
 1 void bfs()
 2 {
 3     for(int i=0;i<=25;i++)
 4     tree[0][i]=1;//把0的所有出边都设为1 
 5     next[1]=0;q.push(1);//把1的next记为0,1号点入队 
 6     while(q.size())
 7     {
 8         int u=q.front();
 9         q.pop();
10         for(int i=0;i<=25;i++)
11         {
12             if(!tree[u][i])
13             tree[u][i]=tree[next[u]][i];//这里就是上文所述的优化 如果u没有i这个儿子,
14             //那就把next[u]的i这个儿子当做u的i这个儿子 
15             else
16             {
17                 q.push(tree[u][i]);
18                 int v=next[u];
19                 next[tree[u][i]]=tree[v][i];
20             }
21         }
22     }
23 }
求next数组
1 void find(char *s)
2 {
3     int u=1,len=strlen(s),k;
4     for(int i=0;i<len;i++)
5     {
6         int c=s[i]-'a';
7         u=tree[u][c];
8     }
9 }
主串匹配

 

接下来是一道模板题:

P3808 【模板】AC自动机(简单版) 

  1 #include<iostream>
  2 #include<string>
  3 #include<cstdio>
  4 #include<cmath>
  5 #include<cstring>
  6 #include<map>
  7 #include<algorithm>
  8 #include<stack>
  9 #include<queue>
 10 #include<vector>
 11 #define maxn 1000005
 12 using namespace std;
 13 
 14 inline int read()
 15 {
 16     int x=1,res=0;
 17     char c=getchar();
 18     while(c<'0'||c>'9')
 19     {
 20         if(c=='-')
 21         x=-1;
 22         c=getchar();
 23     }
 24     while(c>='0'&&c<='9')
 25     {
 26         res=res*10+(c-'0');
 27         c=getchar();
 28     }
 29     return res*x;
 30 }
 31 
 32 int n,tot=1,ans;
 33 char a[maxn];
 34 int tree[maxn][26],next[maxn],bo[maxn];
 35 queue<int>q;
 36 
 37 void trie(char *s)
 38 {
 39     int len=strlen(s),u=1;
 40     for(int i=0;i<len;i++)
 41     {
 42         int c=s[i]-'a';
 43         if(!tree[u][c])
 44         {
 45             tree[u][c]=++tot;
 46         }
 47         u=tree[u][c];
 48     }
 49     bo[u]++;
 50 }
 51 
 52 void bfs()
 53 {
 54     for(int i=0;i<=25;i++)
 55     tree[0][i]=1;
 56     next[1]=0;q.push(1);
 57     while(q.size())
 58     {
 59         int u=q.front();
 60         q.pop();
 61         for(int i=0;i<=25;i++)
 62         {
 63             if(!tree[u][i])
 64             tree[u][i]=tree[next[u]][i];
 65             else
 66             {
 67                 q.push(tree[u][i]);
 68                 int v=next[u];
 69                 next[tree[u][i]]=tree[v][i];
 70             }
 71         }
 72     }
 73 }
 74 
 75 void find(char *s)
 76 {
 77     int u=1,len=strlen(s),k;
 78     for(int i=0;i<len;i++)
 79     {
 80         int c=s[i]-'a';
 81         k=tree[u][c];
 82         while(k>1&&bo[k]!=-1)
 83         {
 84             ans+=bo[k];
 85             bo[k]=-1;
 86             k=next[k];
 87         }
 88         u=tree[u][c];
 89     }
 90 }
 91 
 92 int main()
 93 {
 94     n=read();
 95     for(int i=1;i<=n;i++)
 96     {
 97         scanf("%s",a);
 98         trie(a);
 99     }
100     bfs();
101     scanf("%s",a);
102     find(a);
103     cout<<ans;
104     return 0;
105 }
View Code

这还是一道模板题:

P3796 【模板】AC自动机(加强版) 

  1 #include<iostream>
  2 #include<string>
  3 #include<cstdio>
  4 #include<cmath>
  5 #include<cstring>
  6 #include<map>
  7 #include<algorithm>
  8 #include<stack>
  9 #include<queue>
 10 #include<vector>
 11 #define maxn 1000005
 12 using namespace std;
 13 
 14 struct tr
 15 {
 16     int next;
 17     int vis[26];
 18     int end;
 19     int num;
 20 }tree[20000];
 21 
 22 inline int read()
 23 {
 24     int x=1,res=0;
 25     char c=getchar();
 26     while(c<'0'||c>'9')
 27     {
 28         if(c=='-')
 29         x=-1;
 30         c=getchar();
 31     }
 32     while(c>='0'&&c<='9')
 33     {
 34         res=res*10+(c-'0');
 35         c=getchar();
 36     }
 37     return res*x;
 38 }
 39 
 40 int n,tot,ans;
 41 char b[155][75];
 42 char a[maxn];
 43 int f[20000];
 44 queue<int>q;
 45 
 46 void trie(char *s,int num)
 47 {
 48     int len=strlen(s),u=1;
 49     for(int i=0;i<len;i++)
 50     {
 51         int c=s[i]-'a';
 52         if(!tree[u].vis[c])
 53         {
 54             tree[u].vis[c]=++tot;
 55         }
 56         u=tree[u].vis[c];
 57     }
 58     tree[u].num=num;
 59 }
 60 
 61 void bfs()
 62 {
 63     for(int i=0;i<=25;i++)
 64     tree[0].vis[i]=1;
 65     tree[1].next=0;q.push(1);
 66     while(q.size())
 67     {
 68         int u=q.front();
 69         q.pop();
 70         for(int i=0;i<=25;i++)
 71         {
 72             if(!tree[u].vis[i])
 73             tree[u].vis[i]=tree[tree[u].next].vis[i];
 74             else
 75             {
 76                 q.push(tree[u].vis[i]);
 77                 int v=tree[u].next;
 78                 tree[tree[u].vis[i]].next=tree[v].vis[i];
 79             }
 80         }
 81     }
 82 }
 83 
 84 void find(char *s)
 85 {
 86     int len=strlen(s),u=1,k;
 87     for(int i=0;i<len;i++)
 88     {
 89         int c=s[i]-'a';
 90         k=tree[u].vis[c];
 91         while(k>1)
 92         {
 93             f[tree[k].num]++;
 94             k=tree[k].next;
 95         }
 96         u=tree[u].vis[c];
 97     }
 98 }
 99 
100 int main()
101 {
102     while(1)
103     {
104         n=read();
105         if(n==0) break;
106         memset(tree,0,sizeof(tree));
107         memset(f,0,sizeof(f));
108         tot=1;ans=0;
109         for(int i=1;i<=n;i++)
110         {
111             scanf("%s",b[i]);
112             trie(b[i],i);
113         }
114         bfs();
115         scanf("%s",a);
116         find(a);
117         for(int i=1;i<=n;i++)
118         {
119             ans=max(ans,f[i]);
120         }
121         cout<<ans<<endl;
122         for(int i=1;i<=n;i++)
123         {
124             if(f[i]==ans)
125             printf("%s\n",b[i]);
126         }
127     }
128     return 0;
129 }
View Code

 

posted @ 2019-02-18 09:39  snowy2002  阅读(237)  评论(0编辑  收藏  举报