AC自动机--P3808 【模板】AC自动机(简单版)
AC自动机是建立在KMP算法和Trie树的基础上
一、主要步骤
- 将所有的模式串构建成一个Trie树
- 对Trie树上的所有节点求失配指针(即从根节点出发,一个串是另一个串的后缀,画图比较好理解)
- 利用失配指针对主串进行匹配
代码,附注释,应该会好理解一些:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <queue> 5 #include <cstring> 6 using namespace std; 7 const int maxn=2e6+10; 8 int trie[maxn][30]; 9 int flag[maxn],fail[maxn],cnt; 10 void insert(char *s){//构建模式串的Trie树 11 int root=0,len=strlen(s); 12 for (int i = 0;i < len;i++){ 13 int next=s[i]-'a'; 14 if (!trie[root][next]) trie[root][next]=++cnt; 15 root=trie[root][next]; 16 } 17 flag[root]++; 18 } 19 void getfail(){//求Fail失配数组 20 queue<int> q; 21 for (int i = 0;i < 26;i++){//遍历第一层的26个字母 22 if (trie[0][i]){//如果有这个字母 23 fail[trie[0][i]]=0;//fail指向0 24 q.push(trie[0][i]); 25 } 26 } 27 while (!q.empty()){ 28 int now=q.front(); 29 q.pop(); 30 for (int i = 0;i < 26;i++){ 31 if (trie[now][i]){//如果这一层有这个叶子节点 32 fail[trie[now][i]]=trie[fail[now]][i];//这个叶子节点i的失配指针指向它父亲节点失配指针指向的节点的i儿子,即保证是同一个字母 33 q.push(trie[now][i]); 34 } 35 else trie[now][i]=trie[fail[now]][i];//如果不存在这个i叶子节点,指向当前节点失配指针指向节点的i叶子节点,即保证是同一个字母 36 } 37 } 38 } 39 int query(char *s){//查询一共出现了多少模式串 40 int now=0,ans=0,len=strlen(s); 41 for (int i = 0;i < len;i++){//遍历文本串 42 now=trie[now][s[i]-'a']; 43 for (int j = now;j&&flag[j]!=-1;j=fail[j]){ 44 //一直向下寻找,直到匹配失败(失配指针指向根或者当前节点已经找过) 45 ans+=flag[j]; 46 flag[j]=-1; 47 } 48 } 49 return ans; 50 } 51 int n; 52 char s[maxn]; 53 int main(){ 54 scanf ("%d",&n); 55 for (int i = 0;i < n;i++) { 56 scanf ("%s",s); 57 insert(s); 58 } 59 fail[0]=0; 60 getfail(); 61 scanf ("%s",s); 62 printf("%d\n",query(s)); 63 return 0; 64 }