HDU 2222 题意:给出N(N<=10,000)个单词,每个单词长度不超过50。再给出一个字符串S,字符串长度不超过1,000,000。问有多少个单词出现在了字符串S中。(单词可能重复,单词在S中允许重叠)
我们在val[]数组中记录重复的个数
因为在主串对他进行匹配时,匹配到一次后这个重复次数就不应该继续添加了,所以在query中val[]要对它进行清零操作
#include <cstdio> #include <cstring> #include <queue> using namespace std; #define N 500005 char str[1000005]; struct AC{ int ch[N][26],fail[N],val[N],last[N],tmp,root; int newnode(){ val[tmp]=0; memset(ch[tmp],0,sizeof(ch[tmp])); return tmp++; } void init(){ tmp=0; root=newnode(); } void add(char *s){ int len=strlen(s); int now=root; for(int i=0;i<len;i++){ int &k=ch[now][s[i]-'a']; if(!k) k=newnode(); now=k; } val[now]++; } void get_fail(){ fail[root]=root; queue<int> q; for(int i=0;i<26;i++){ int v=ch[root][i]; if(v) fail[v]=last[v]=0,q.push(v); } while(!q.empty()){ int now=q.front(); q.pop(); for(int i=0;i<26;i++){ int v=ch[now][i]; if(!v) ch[now][i]=ch[fail[now]][i]; else{ fail[v]=ch[fail[now]][i]; last[v]=val[fail[v]]?fail[v]:last[fail[v]]; q.push(v); } } } } int query(char *str){ int len=strlen(str); int now=root,ret=0; for(int i=0;i<len;i++){ now=ch[now][str[i]-'a']; int k=now; while(k!=root&&val[k]){//这是要循环到找到fail值为root的时候或者找到匹配的字符串的时候,否则一直向前找fail值, ret+=val[k]; val[k]=0; k=last[k]; } } return ret; } }ac; int main() { int T,n; scanf("%d",&T); while(T--){ ac.init(); scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%s",str); ac.add(str); } ac.get_fail(); scanf("%s",str); int ans=ac.query(str); printf("%d ",ans); } return 0; }