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\n",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\n",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 }指针版
标签:AC,int,2222,HDU,next,trie,fail,son,root 来源: https://www.cnblogs.com/OI-zzyy/p/11108708.html