HDU 2222 Keywords Search(AC自动机)

题干:

  给定 n 个长度不超过 50 的由小写英文字母组成的单词准备查询,以及一篇长为m的文章,问:文中出现了多少个待查询的单词。多组数据

题解:

  这是一道AC自动机的裸题(尽管你可以用hash+卡常A掉它。。。)。

  AC自动机其实也就两个核心部分:fail指针(或数组)、trie图

  1、fail指针(或数组)

  其实AC自动机类似于KMP算法,由KMP的单模式匹配优化为了多模式匹配。在KMP中,我们维护了一个Next[]数组(注意在NOIP中,next为保留字),他记录的就是当这一位失配后,最近的上一位匹配到的位置。而AC自动机也沿袭了这一思想,出现了fail数组(或fail指针),用来指向其它可与目标串部分匹配(至少从fail指向的那个节点开始,到根都是匹配的)的另一个串的一个节点。

  2、trie图

  trie图是在trie树基础上为了降低时间复杂度而出现一种技巧(可能你手中的模板就是trie图的。。。)。它的核心就是当x没有y这个儿子时,将x节点的不存在的儿子节点y,用fail[x]的对应儿子节点y'补齐,这样就在查询时节省了再跑回根节点在匹配的不必要的跑路时间。因为在连边(补齐儿子)的过程中,我们将树的性质破坏,变成了图,我们就将它叫做trie图。

 

Code

  下面附上个人代码,不要随便颓代码。。。

 1 #include<cstring>
 2 #include<queue>
 3 #include<cstdio>
 4 #define   up(i,x,y,z) for(register int i=(x);i<=(y);i+=(z))
 5 #define down(i,x,y,z) for(register int i=(x);i>=(y);i-=(z))
 6 #define $ 511111
 7 using namespace std;
 8 char str[$*2];
 9 int m,n,tot,t;
10 struct trie{
11     int next[$][26],fail[$],end[$],count[$],root,length;
12     inline int NewTrie(){
13         up(i,0,25,1) next[length][i]=-1;
14         end[length]=-1;
15         count[length]=0;
16         return length++;
17     }
18     inline void init(){    length=0; root=NewTrie();    }
19     inline void insert(char *s,int id){
20         int len=strlen(s);
21         int x=root;
22         up(i,0,len-1,1){
23             if(next[x][s[i]-'a']==-1)  next[x][s[i]-'a']=NewTrie();
24             x=next[x][s[i]-'a'];
25         }
26         end[x]=id;
27         count[x]++;
28     }
29     inline void build(){
30         queue<int> q;
31         fail[root]=root;
32         up(i,0,25,1){
33             if(next[root][i]==-1)  next[root][i]=root;
34             else fail[next[root][i]]=root,q.push(next[root][i]);
35         }
36         while(q.size()){
37             int x=q.front(); q.pop();
38             up(i,0,25,1){
39                 if(next[x][i]==-1) next[x][i]=next[fail[x]][i];
40                 else fail[next[x][i]]=next[fail[x]][i],q.push(next[x][i]);
41             }
42         }
43     }
44     
45     inline int query(char *str,int n,int id){
46         int len=strlen(str),x=root,ans=0,temp;
47         up(i,0,len-1,1){
48             x=next[x][str[i]-'a'];
49             temp=x;
50             while(temp!=root){
51                 ans+=count[temp];
52                 count[temp]=0;
53                 temp=fail[temp];
54             }
55         }
56         return ans;
57     }
58 }AC;
59 signed main(){
60     scanf("%d",&t);
61     while(t--){
62         scanf("%d",&n);
63         AC.init();
64         up(i,1,n,1) scanf("%s",str),AC.insert(str,i);
65         AC.build();
66         scanf("%s",str);
67         printf("%d
",AC.query(str,n,1));
68     }
69 }
数组版
 1 #include<cstdio>
 2 #include<queue>
 3 #include<cstring>
 4 #define $ 10000000 
 5 using namespace std;
 6 int m,n,t;
 7 char s[$],print[$];
 8 struct trie{    int sum;  trie *son[27],*fail;    };///4096
 9 inline trie *newnode(){
10     trie *p=new trie;
11     for(register int i=0;i<26;++i) p->son[i]=NULL;
12     p->fail=NULL;    p->sum=0;
13     return p;
14 }
15 inline void insert(trie *root){
16     trie *p=root;
17     for(register int i=1;i<=strlen(s+1);++i){
18         int x=s[i]-'a';
19         if(p->son[x]==NULL) p->son[x]=newnode();
20         p=p->son[x];
21     }
22     p->sum++;
23 }
24 inline void build(trie *root){
25     queue<trie*> q;
26     for(register int i=0;i<26;++i){
27         if(root->son[i]) root->son[i]->fail=root,q.push(root->son[i]);
28         else root->son[i]=root;
29     }
30     while(q.size()){
31         trie *p=q.front(); q.pop();
32         for(register int i=0;i<26;++i){
33             if(p->son[i]){
34                 p->son[i]->fail=p->fail->son[i];
35                 q.push(p->son[i]);
36             }
37             else p->son[i]=p->fail->son[i];
38         }    
39     }
40 }
41 inline void ac(trie *root){
42     int ans=0,len=strlen(s+1);
43     trie *p=root;
44     for(register int i=1;i<=len;++i){
45         int x=s[i]-'a';
46         while(p->son[x]==NULL&&p!=root) p=p->son[x];
47         p=p->son[x];
48         trie *pp=p;
49         while(pp->sum!=-1&&pp!=root){
50             ans+=pp->sum;
51             pp->sum=-1;
52             pp=pp->fail;
53         }
54     }
55     printf("%d
",ans);
56 }
57 inline void work(){
58     trie *root=newnode();
59     scanf("%d",&n);
60     for(register int i=1;i<=n;++i) scanf("%s",s+1),insert(root);
61     build(root);
62     scanf("%s",s+1);  ac(root);
63 }
64 signed main(){
65     scanf("%d",&t);
66     while(t--) work();
67 }
指针版
越努力 越幸运
原文地址:https://www.cnblogs.com/OI-zzyy/p/11108708.html