【模板】 ac自动机

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm> 
 4 #include<queue>
 5 #define maxn 5000000+10
 6 using namespace std;
 7 char str[maxn*2];
 8 struct node{
 9     int fail;//失配指针;
10     int cnt;//单词出现的次数;
11     int next[62];// 此节点的下一个(儿子)节点; 
12 }trie[maxn];//节点结构体; 
13 int k=0,ans=0;
14 queue<int> q;//队列:建失配指针使用; 
15 void build_trie(int id,char *s)//id表示第几个结点,即所有字符串从第0个节点开始向下建;*s即表示这个字符串; 
16 {
17     int len=strlen(s);//该字符串的长度;(相当于字符串最后一个字符的深度)
18     int j=0;
19     for(int i=0;i<len;i++){
20         j=s[i]-'a';
21         if(trie[id].next[j]==0)/*若此字母未出现在当前位置的下一深度*/
22         {
23             trie[id].next[j]=++k;//当前节点对于j字母节点的位置;即j字母的节点序号; 
24         }
25         id=trie[id].next[j];//id传为下一字母的地址; 
26     }
27     trie[id].cnt++;//对此单词的数量++; 
28 }
29 void build_fail(int id)
30 {
31     while(!q.empty()) q.pop();//为了放心,(be afraid of队列未清空)
32     for(int i=0;i<26;i++)//遍历超级节点下的26个字母;
33     {
34         int j=trie[id].next[i];
35         if(j!=0){
36             q.push(j);
37             trie[j].fail=id;//第一层的节点失配指针皆指向超级节点; 
38         }
39     }
40     while(!q.empty())
41     {
42         int now=q.front();q.pop();//取出当前位置;(队首元素) 
43         for(int i=0;i<26;i++)
44         {
45             int j=trie[now].next[i];
46             if(j==0)//当前位置下没有这个节点;就调到它失配指针所指向的节点下的此个字母节点; 
47             {
48                 trie[now].next[i]=trie[trie[now].fail].next[i];//若为0,不影响,指向超级节点; 
49                 continue;//该点遍历完成,直接进入下一节点的遍历; 
50             }
51             trie[j].fail=trie[trie[now].fail].next[i];//如果当前位置下有这个字母节点,则其失配指针指向当前位置的失配指针下的该节点;
52             //若当前位置的失配指针下没有当前遍历的该字母节点,任不影响(为0,指向超级节点); 
53             q.push(j);//存入数组; 
54         }
55     }
56 }
57 void solve_trie(int id,char *s)//查询函数; 
58 {
59     int len=strlen(s),j=0;
60     for(int i=0;i<len;i++)
61     {
62         int j=trie[id].next[s[i]-'a'];//当前位置的下一个节点位置;
63         while(j && trie[j].cnt!=-1)//当此节点存在同时其cnt未被遍历; 
64         {
65             ans+=trie[j].cnt;//将答案加上所搜索的字符串中所包含的该单词数 ; 
66             trie[j].cnt=-1;//标记(因为是看有多少个模式串出现过,而不是出现多少次)
67             j=trie[j].fail;//直接将位置指向其失配指针的位置(节约时间)(也就是找匹配了的模式串的fail)(就是删除模式串前面的一部分的模式串) 
68             //一个fail就是把匹配的串的前面一部分删去,而建立的字典树是模式串
69         }
70         id=trie[id].next[s[i]-'a'];//id继承,当前位置的下一个节点; 0也没关系
71     }
72 }
73 int main()
74 {
75     int n;
76     scanf("%d",&n);
77     for(int i=1;i<=n;i++)
78     {
79         scanf("%s",str);//第i个短字符串;
80         build_trie(0,str);//0为超级根节点(即所有字符串的共同祖先);(即从0开始建) 
81     }//字典树建立完成;
82     build_fail(0);//建失配指针; 
83     scanf("%s",str);//输入需查询的字符串;
84     solve_trie(0,str); //查询; 
85     printf("%d\n",ans);//输出; 
86     return 0;
87 }
88 //https://www.luogu.org/problemnew/solution/P3808
89 //还有最好用gets

猜你喜欢

转载自www.cnblogs.com/xiaobuxie/p/11391875.html