HDU 2222 Keywords Search (AC自动机模板题)
题目链接:HDU 2222
终于看明白AC自动机了,自己敲的模板,1A。
首先在Trie树上”挂“上所有的单词。
然后在树上构造fail指针。
Trie树上每一个字母的fail指针要么指向父节点,要么指向root,如果其父节点上有相同的字母,指向父节点对应的字母,否则指向root;
可以看出需要一层一层的构建,所以需要用到队列BFS。
查询操作,如果顺着字典树找没有这个字母,就通过fail指针去找,直到找到或者找到root。
如果找到,并且字母为单词结尾,则cnt++,然后通过fail指针继续匹配看是否仍有可以匹配的单词。
源代码
#include <iostream> #include <cstring> #include <cstdio> using namespace std; struct node{ node *next[26]; int count ; //记录 node* fail; node(){ count = 0; fail = NULL; memset(next,0,sizeof(next)); } }*q[5000000]; int head,tail; char str[1000010]; node *root; void insert(char *s){ //构建trie int len = strlen(s); node *p = root; for(int i=0;i<len;i++){ int index = s[i]-'a'; if(!p->next[index]) p->next[index] = new node; p=p->next[index]; } p->count++; } void build_ac_automation(){ //初始化fail指针 q[tail++] = root; while(head<tail){ node *p = q[head++]; node *tmp = NULL; for(int i=0;i<26;i++){ if(p->next[i] != NULL){ if(p == root)//首元素必须指根 p->next[i]->fail = root; else{ tmp = p->fail; //失败指针(跳转指针) while(tmp != NULL){ if(tmp->next[i] != NULL){//找到匹配 p->next[i]->fail = tmp->next[i]; break; } //如果没找到,则继续向上一个失败指针找 tmp = tmp->fail; } if(tmp == NULL) //为空 则从头匹配 p->next[i]->fail = root; } q[tail++] = p->next[i];//下一层入队 } } } } int query(){ int len = strlen(str); node *p = root; int cnt = 0; for(int i=0;i<len;i++){ int index = str[i]-'a'; while(p->next[index] == NULL && p!=root) p = p->fail; p = p->next[index]; if(p == NULL) p = root; node *tmp = p;//tmp 动 , p不动。 while(tmp != root && tmp->count != -1){ cnt += tmp->count; tmp->count = -1; tmp = tmp ->fail; } } return cnt; } int main(){ int t; scanf("%d",&t); while(t--){ head= tail = 0; root = new node; int n; scanf("%d",&n); char s[10010]; for(int i=0;i<n;i++){ scanf("%s",s); insert(s); } build_ac_automation(); scanf("%s",str); int ans = query(); printf("%d\n",ans); } return 0; }