hdu2222 Keywords Search(AC自动机初步)

题目大意:

  给出多个模式串和一个主串,求多少个模式串在主串中出现过。


  传送门

这是一道AC自动机的模板题。

在学习AC自动机之前,首先要学习WA自动机、TLE自动机和MLE自动机(雾

AC自动机是一种多模式串匹配算法。

AC自动机概述:

  *fail指针:指向失配时的匹配节点;

  1)构建字典树

  2)初始化fail指针:

    一条$fail$指针链可以理解为一个串连所有后缀相同的字符串的链表,并且所有链表的末端都指向trie的根。我们定义沿着节点$v$的$fail$指针走到根部的路径为v的trie链表

    虚线为$fail$指针,红色节点为AC-DFA的接收态。

    使用BFS构造fail指针:初始化队列为树根。对于trie上的每个非叶子节点$u$的孩子$v$,我们沿着其fail链表向上走,直到某个节点的孩子$x=v$,则$v->fail=x$。将$v$加入队列。

    特别的,对于$p->nxt[i]==NULL$的节点,令$p->nxt[i]=p->fail->nxt[i]$

  3)由于初始化$fail$指针的时候,令$p->nxt[i]=p->fail->nxt[i]$,所以现在的trie树变成了一个trie图,所有节点的$nxt$指针都不为空。我们按照主串,沿着相应的路径向走trie图,每访问一个节点就遍历该节点的$fail$链表并计数。

代码:

 1 #include<cstring>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cctype>
 6 #define foru(i,x,y) for(int i=x;i<=y;i++)
 7 const int N=1e6+10;
 8 
 9 struct trie{
10     trie *nxt[26];
11     trie *fail;
12     int v;
13     void init(){v=0;foru(i,0,25)nxt[i]=NULL;fail=NULL;}
14 };
15 trie *r,*q[600000];
16 int T,n;
17 char ch[N];
18 void add(char *s){//建trie 
19     trie *k=r,*p;
20     int l=strlen(s);
21     foru(i,0,l-1){
22         int id=s[i]-'a';
23         if(!k->nxt[id]){
24             p=(trie*)malloc(sizeof(trie));
25             p->init();
26             k->nxt[id]=p;
27             k=p;
28         }else{
29             k=k->nxt[id];
30         }
31     }
32     k->v++;//标记为接收态 
33 }
34 
35 void setfail(){
36     trie *k;
37     int s=1,t=0;
38     q[++t]=r;
39     while(s<=t){
40         k=q[s++];
41         foru(i,0,25){
42             if(k->nxt[i]){
43                 trie *p=k->fail;
44                 while(p&&!p->nxt[i])p=p->fail;//寻找上一个相同后缀的字符串 
45                 k->nxt[i]->fail=(!p?r:p->nxt[i]);//不存在则指向trie根 
46                 q[++t]=k->nxt[i];
47             }else
48                 k->nxt[i]=(k==r?r:k->fail->nxt[i]);
49         }
50     }
51 }
52 
53 int find(char *s){
54     trie *k=r,*p;int ret=0;
55     int l=strlen(s);
56     foru(i,0,l-1){
57         int id=s[i]-'a';
58         k=k->nxt[id];
59         p=k;
60         while(p){//遍历fail链 
61             ret+=p->v;
62             p->v=0;//避免重复计数 
63             p=p->fail;
64         }
65     }
66     return ret;
67 }
68 
69 int main(){
70     scanf("%d",&T);
71     while(T--){
72         r=(trie*)malloc(sizeof(trie));
73         r->init();
74         scanf("%d",&n);
75         foru(i,1,n){
76             scanf("%s",ch);
77             add(ch);
78         }
79         setfail();
80         scanf("%s",ch);
81         printf("%d\n",find(ch));
82     }
83 }

 

posted @ 2017-09-06 13:24  羊毛羊  阅读(430)  评论(0编辑  收藏  举报