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

AC自动机是建立在KMP算法和Trie树的基础上

一、主要步骤

  1. 将所有的模式串构建成一个Trie树
  2. 对Trie树上的所有节点求失配指针(即从根节点出发,一个串是另一个串的后缀,画图比较好理解)
  3. 利用失配指针对主串进行匹配

代码,附注释,应该会好理解一些:

 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 }

 

posted @ 2020-09-30 07:55  小又又  阅读(151)  评论(0编辑  收藏  举报