本文参考博客:
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/
代码如下:(这道题时间卡的特别严格)
先附上时间被卡的代码,手写了一个,竟被卡掉:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef unsigned int ui; 4 typedef long long ll; 5 typedef unsigned long long ull; 6 #define pf printf 7 #define mem(a,b) memset(a,b,sizeof(a)) 8 #define prime1 1e9+7 9 #define prime2 1e9+9 10 #define pi 3.14159265 11 #define lson l,mid,rt<<1 12 #define rson mid+1,r,rt<<1|1 13 #define scand(x) scanf("%llf",&x) 14 #define f(i,a,b) for(int i=a;i<=b;i++) 15 #define scan(a) scanf("%d",&a) 16 #define dbg(args) cout<<#args<<":"<<args<<endl; 17 #define inf 0x3f3f3f3f 18 const int maxn=5e5+10; 19 int n,m,t,tot=0; 20 char s[maxn]; 21 int trie[maxn][26],fail[maxn],cnt[maxn]; 22 void insert(char* s) 23 { 24 int rt=0,len=strlen(s); 25 f(i,0,len-1) 26 { 27 char c=s[i]-‘a‘; 28 if(!trie[rt][c])trie[rt][c]=++tot; 29 rt=trie[rt][c]; 30 } 31 cnt[rt]++;//标记单词的尾部 32 } 33 void getfail() 34 { 35 queue<int>q; 36 f(i,0,25) 37 { 38 if(trie[0][i]) 39 { 40 q.push(trie[0][i]);//第一层结点入队 41 fail[trie[0][i]]=0; 42 } 43 //fail[now] ->当前节点now的失败指针指向的地方 44 //tire[now][i] -> 下一个字母为i+‘a‘的节点的下标为tire[now][i] 45 while(!q.empty()) 46 { 47 int cur=q.front(); 48 q.pop(); 49 f(i,0,25) 50 { 51 if(trie[cur][i]) 52 { 53 fail[trie[cur][i]]=trie[fail[cur]][i]; 54 q.push(trie[cur][i]); 55 } 56 else trie[cur][i]=trie[fail[cur]][i]; 57 } 58 } 59 } 60 } 61 int query(char* s) 62 { 63 int len=strlen(s); 64 int now=0,ans=0; 65 f(i,0,len-1) 66 { 67 now=trie[now][s[i]-‘a‘]; 68 for(int j=now;j&&cnt[j]!=-1;j=fail[j]) 69 { 70 ans+=cnt[j]; 71 cnt[j]=-1;//检查过的点就标记为已知,防止多次检查 72 } 73 } 74 return ans; 75 } 76 int main() 77 { 78 //freopen("input.txt","r",stdin); 79 //freopen("output.txt","w",stdout); 80 std::ios::sync_with_stdio(false); 81 scan(t); 82 while(t--) 83 { 84 f(i,0,maxn-1)cnt[i]=0,fail[i]=0; 85 f(i,0,maxn-1) 86 f(j,0,25)trie[i][j]=0; 87 tot=0; 88 scan(n); 89 f(i,1,n) 90 { 91 scanf("%s",&s); 92 insert(s); 93 } 94 getfail(); 95 scanf("%s",&s); 96 pf("%d\n",query(s)); 97 } 98 }
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\n", ans); } return 0; }
原文:https://www.cnblogs.com/randy-lo/p/12543159.html