本文参考博客:
https://blog.csdn.net/bestsort/article/details/82947639
https://blog.csdn.net/creatorx/article/details/71100840
先解释一下自动机,一般我们所说的自动机都是有限状态自动机,所有的状态都属于一个状态集S,另外有转移函数F,相当于是一种刺激,使得当前自动机的状态可以转移到另一个状态。AC自动机就是在Trie上加上了fail指针,fail指针是多模式匹配的关键,而KMP适用于单模式匹配。fail使得Trie树转变成为Trie图。AC自动机中用模式串建Trie树,然后用文本串进行匹配,所以复杂度为O(max{m*|P|,|S|})。
下图可以简明的表示通过模式串构建成功的AC自动机的样子:
红色圈标志单词的存在性,也就是树中存在的模式串,虚线就是fail指针的指向,fail指针的设计可以用图的层次遍历实现。fail其实就像KMP的next数组,这里的fail指向与当前结点有后缀的最深的路径的终点。
匹配过程的重点有两个,1、,匹配成功,通过不断向下扩展达到红色结点(标记了单词的结尾),每次扩展到下一层之前不断通过fail指针向上转移,如果fail指针没有指向根节点,那么他代表的串一定是转移前结点代表的串的后缀,并且查看该后缀是否是单词 2、从now结点开始失配,从失配的位置转移到fail指针指向的下一个位置(注意,这个转移在fail构建的时候就已经设置好了,所以如果下个字符在fail指向结点的下一个位置匹配,则now也转移,否则失配,转到根节点),如上图,匹配shers到she失败后匹配最左边路径的rs路径。
hdu2222是AC自动机的模板题,题目链接:http://icpc.njust.edu.cn/Problem/Hdu/2222/
代码如下:(这道题时间卡的特别严格,写的代码竟被卡掉)
kuangbin代码:
1 //====================== 2 // HDU 2222 3 // 求目标串中出现了几个模式串 4 //==================== 5 #include <stdio.h> 6 #include <algorithm> 7 #include <iostream> 8 #include <string.h> 9 #include <queue> 10 using namespace std; 11 12 struct Trie 13 { 14 int next[500010][26],fail[500010],end[500010]; 15 int root,L; 16 int newnode() 17 { 18 for(int i = 0;i < 26;i++) 19 next[L][i] = -1; 20 end[L++] = 0; 21 return L-1; 22 } 23 void init() 24 { 25 L = 0; 26 root = newnode(); 27 } 28 void insert(char buf[]) 29 { 30 int len = strlen(buf); 31 int now = root; 32 for(int i = 0;i < len;i++) 33 { 34 if(next[now][buf[i]-'a'] == -1) 35 next[now][buf[i]-'a'] = newnode(); 36 now = next[now][buf[i]-'a']; 37 } 38 end[now]++; 39 } 40 void build() 41 { 42 queue<int>Q; 43 fail[root] = root; 44 for(int i = 0;i < 26;i++) 45 if(next[root][i] == -1) 46 next[root][i] = root; 47 else 48 { 49 fail[next[root][i]] = root; 50 Q.push(next[root][i]); 51 } 52 while( !Q.empty() ) 53 { 54 int now = Q.front(); 55 Q.pop(); 56 for(int i = 0;i < 26;i++) 57 if(next[now][i] == -1) 58 next[now][i] = next[fail[now]][i]; 59 else 60 { 61 fail[next[now][i]]=next[fail[now]][i]; 62 Q.push(next[now][i]); 63 } 64 } 65 } 66 int query(char buf[]) 67 { 68 int len = strlen(buf); 69 int now = root; 70 int res = 0; 71 for(int i = 0;i < len;i++) 72 { 73 now = next[now][buf[i]-'a']; 74 int temp = now; 75 while( temp != root ) 76 { 77 res += end[temp]; 78 end[temp] = 0; 79 temp = fail[temp]; 80 } 81 } 82 return res; 83 } 84 void debug() 85 { 86 for(int i = 0;i < L;i++) 87 { 88 printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]); 89 for(int j = 0;j < 26;j++) 90 printf("%2d",next[i][j]); 91 printf("] "); 92 } 93 } 94 }; 95 char buf[1000010]; 96 Trie ac; 97 int main() 98 { 99 int T; 100 int n; 101 scanf("%d",&T); 102 while( T-- ) 103 { 104 scanf("%d",&n); 105 ac.init(); 106 for(int i = 0;i < n;i++) 107 { 108 scanf("%s",buf); 109 ac.insert(buf); 110 } 111 ac.build(); 112 scanf("%s",buf); 113 printf("%d ",ac.query(buf)); 114 } 115 return 0; 116 }
AC代码:
#include<iostream> #include<algorithm> #include<string.h> #include<stdio.h> #include<stdlib.h> #include<vector> #include<queue> #include<map> #include<set> using namespace std; const int N = 26; const int MAXN = 500000 + 10; struct node { int next[MAXN][N], fail[MAXN], endd[MAXN]; int root, l; int newnode() { for (int i = 0; i < N; i++) next[l][i] = 0; endd[l] = fail[l++] = 0; return l - 1; } void init() { l = 0; root = newnode(); } void Insert(char s[]) { int len = strlen(s); int now = root; for (int i = 0; i < len; i++) { if (next[now][s[i] - 'a'] == 0) next[now][s[i] - 'a'] = newnode(); now = next[now][s[i] - 'a']; } endd[now]++; } void build() { queue<int>qu; int now = root; for (int i = 0; i < N; i++) { if (next[root][i]) qu.push(next[root][i]); } while (!qu.empty()) { now = qu.front(); qu.pop(); for (int i = 0; i < N; i++) { if (!next[now][i]) next[now][i] = next[fail[now]][i]; else { fail[next[now][i]] = next[fail[now]][i]; qu.push(next[now][i]); } } } } int query(char s[]) { int len = strlen(s); int now = root; int ret = 0; for (int i = 0; i < len; i++) { now = next[now][s[i] - 'a']; int tmp = now; while (tmp != root) { ret += endd[tmp]; endd[tmp] = 0; tmp = fail[tmp]; } } return ret; } }; node Aho; int T, n; char buf[MAXN], str[MAXN * 2]; int main() { scanf("%d", &T); while (T--) { scanf("%d", &n); Aho.init(); for (int i = 0; i < n; i++) { scanf("%s", buf); Aho.Insert(buf); } Aho.build(); scanf("%s", str); int ans = Aho.query(str); printf("%d ", ans); } return 0; }