题目大意:
给你一些单词,和一个字符串,问你这个字符串中含有多少个上面的单词。
解题分析:
这是多模匹配问题,如果用KMP的话,对每一个单词,都跑一遍KMP,那么当单词数量非常多的时候,耗时会非常多,所以这里用到了AC自动机,这是一种类似于Trie树的数据结构,但是同时,它也用到了KMP算法中 next数组的思想。
下面是AC自动机指针形式的题解:
#include <iostream> #include <cstring> #include <string.h> #include <algorithm> using namespace std; const int N =10000*50+10; struct node { int cnt; node *fail; node *next[26]; node() { cnt=0; fail=NULL; for(int i=0;i<26;i++) next[i]=NULL; } }*que[N]; node *root; void ins(char *s) { int len = strlen(s); node *tmp,*rt=root; for(int i=0;i<len;i++) { int id=s[i]-‘a‘; if(rt->next[id]==NULL) rt->next[id]=new node(); rt=rt->next[id]; } rt->cnt++; } void build_AC() { int st=0,ed=0; que[ed++]=root; while (st < ed) { node *tmp, *rt=que[st++]; for(int i=0;i<26;i++) { if(rt->next[i]!=NULL) { if(rt==root) //单个字符,没有后缀,因此失配指向root rt->next[i]->fail=root; else { //不断找父亲的fail指针下面有next[id]的后缀 tmp = rt->fail; while (tmp!=NULL) { if(tmp->next[i]!=NULL) { rt->next[i]->fail = tmp->next[i]; break; } tmp=tmp->fail; } if(tmp==NULL) rt->next[i]->fail=root; } que[ed++]=rt->next[i]; } } } } int match(char *s) { node *rt=root; int len=strlen(s); int res=0; for(int i=0;i<len;i++) { int id= s[i]-‘a‘; if(rt->next[id]!=NULL) rt=rt->next[id]; else { rt=rt->fail; while (rt!=NULL && rt->next[id]==NULL) rt=rt->fail; if(rt != NULL) rt=rt->next[id]; else rt=root; } node *tmp = rt; if(tmp->cnt > 0) { while (tmp!=NULL) { res += tmp->cnt; tmp->cnt=0; tmp = tmp->fail; } } } return res; } int T,n; char word[1000000+10],str[60]; int main () { scanf("%d",&T); while (T--) { root=new node(); scanf("%d",&n); for(int i=0;i<n;i++) scanf("%s",str),ins(str); build_AC(); scanf("%s",word); printf("%d\n",match(word)); } return 0; }
参考blog/资料:
https://www.bilibili.com/video/av6295004
https://www.cnblogs.com/00isok/p/9426990.html
原文:https://www.cnblogs.com/Draymonder/p/9503316.html