原题大意:原题链接
先给定T个单词,然后给定一个字符串,查询该字符串中包含多少个给定的单词
解题思路:AC自动机模板题
WA版本
注意:因为输入的单词可能有重复,那么Insert()函数中p->id=id;语句中p->id会被覆盖,在Query()函数中会一次性全部被清零,导致不能查询重复单词,以至于结果res错误.
#include<queue> #include<cstdio> #include<cstring> using namespace std; int T,n; char word[60],str[1000010]; struct TrieNode { int id; TrieNode *next[26],*fail; TrieNode(){ id=0,fail=NULL; memset(next,0,sizeof(next)); } }*root; void Insert(char *word,int id) { TrieNode *p=root; int len=strlen(word); for(int i=0;i<len;i++){ if(!p->next[word[i]-'a']) p->next[word[i]-'a']=new TrieNode(); p=p->next[word[i]-'a']; } p->id=id; } void Build_AC() { TrieNode *p,*next; queue<TrieNode* > que; que.push(root); while (!que.empty()){ p=que.front(); que.pop(); for(int i=0;i<26;i++){ if(p->next[i]){ if(p==root) p->next[i]->fail=root; else{ TrieNode *temp=p->fail; while(temp){ if(temp->next[i]){//temp始终代表next[i]的爸爸,有next[i]这个儿子 p->next[i]->fail=temp->next[i]; break;//寻找最长后缀 } temp=temp->fail; } if(!temp) p->next[i]->fail=root; } que.push(p->next[i]); } } } } int Query() { TrieNode *p=root; int res=0,len=strlen(str); for(int i=0;str[i];i++){ int jd=str[i]-'a'; while(!p->next[jd]&&p!=root)//当p为根节点时失配边仍根节点会死循环 p=p->fail; p=p->next[jd]; if(!p) p=root; TrieNode *temp=p; while(temp!=root&&temp->id){ res++; temp->id=0; temp=temp->fail; } } return res; } int main() { scanf("%d",&T); while(T--){ root=new TrieNode(); scanf("%d\n",&n); for(int i=1;i<=n;i++){ gets(word); Insert(word,i); } Build_AC(); scanf("%s",str); printf("%d\n",Query()); } return 0; }
AC版本
#include<queue> #include<cstdio> #include<cstring> using namespace std; int T,n; char word[60],str[1000010]; struct TrieNode { int id; TrieNode *next[26],*fail; TrieNode(){ id=0,fail=NULL; memset(next,0,sizeof(next)); } }*root; void Insert(char *word) { TrieNode *p=root; int len=strlen(word); for(int i=0;i<len;i++){ if(!p->next[word[i]-'a']) p->next[word[i]-'a']=new TrieNode(); p=p->next[word[i]-'a']; } p->id++; } void Build_AC() { TrieNode *p,*next; queue<TrieNode* > que; que.push(root); while (!que.empty()){ p=que.front(); que.pop(); for(int i=0;i<26;i++){ if(p->next[i]){ if(p==root) p->next[i]->fail=root; else{ TrieNode *temp=p->fail; while(temp){ if(temp->next[i]){//temp始终代表next[i]的爸爸,有next[i]这个儿子 p->next[i]->fail=temp->next[i]; break;//寻找最长后缀 } temp=temp->fail; } if(!temp) p->next[i]->fail=root; } que.push(p->next[i]); } } } } int Query() { TrieNode *p=root; int res=0,len=strlen(str); for(int i=0;str[i];i++){ int jd=str[i]-'a'; while(!p->next[jd]&&p!=root)//当p为根节点时失配边仍根节点会死循环 p=p->fail; p=p->next[jd]; if(!p) p=root; TrieNode *temp=p; while(temp!=root&&temp->id!=-1){ res+=temp->id; temp->id=-1; temp=temp->fail; } } return res; } int main() { scanf("%d",&T); while(T--){ root=new TrieNode(); scanf("%d\n",&n); for(int i=1;i<=n;i++){ gets(word); Insert(word); } Build_AC(); scanf("%s",str); printf("%d\n",Query()); } return 0; }