题意:给你多个不重复的单词和一个文本串,让你求文本串中出现了哪些单词,各出现了多少次。(没出现的不用输出)
思路:AC自动机基本应用。直接建立AC自动机,节点val[u]表示u节点的单词编号。注意:没有重复的单词。所以找到一个单词时直接cnt[val[u]]++即可,u为该单词的尾节点编号。最后按顺序把出现了的单词输出。
若本题中有重复的模板单词,则需要建立一个map映射,将所有单词映射到它最后一次输入时对应的编号i,最终通过map来获取该单词的编号,从而获取出现的次数。
AC代码:
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<string>
#include<queue>
#include<vector>
#include<map>
#include<set>
using namespace std;
const int mx=60000;
const int ssize=128;
int ans;
struct ACzdj{
int ch[mx][ssize];
int val[mx];
int f[mx];
int last[mx];
int cnt[mx];
int sz;
void init()
{
memset(ch[0],0,sizeof(ch[0]));
memset(cnt,0,sizeof(cnt));
val[0]=0;
f[0]=last[0]=0;
sz=1;
}
void insert(char *s,int v)
{
int n=strlen(s),u=0;
for(int i=0;i<n;i++)
{
int id=s[i];
if(ch[u][id]==0) {
ch[u][id]=sz;
memset(ch[sz],0,sizeof(ch[sz]));
val[sz++]=0;
}
u=ch[u][id];
}
val[u]=v;
}
void print(int i,set<int> &st)
{
if(val[i])
{
cnt[val[i]]++;
print(last[i],st);
}
}
void find(char *s,set<int> &st)
{
int n=strlen(s),j=0;
for(int i=0;i<n;i++)
{
int id=s[i];
while(j&&ch[j][id]==0) j=f[j];
j=ch[j][id];
if(val[j]) print(j,st);
else if(last[j]) print(last[j],st);
}
}
void getFail()
{
queue<int> q;
f[0]=0;
for(int i=0;i<ssize;i++)
{
int u=ch[0][i];
if(u)
{
last[u]=f[u]=0;
q.push(u);
}
}
while(!q.empty())
{
int r=q.front();q.pop();
for(int i=0;i<ssize;i++)
{
int u=ch[r][i];
if(!u) continue;
q.push(u);
int v=f[r];
while(v&&ch[v][i]==0) v=f[v];
f[u]=ch[v][i];
last[u]=val[f[u]]?f[u]:last[f[u]];
}
}
}
}ac;
char txt[2000010];
char ss[1010][60];
set<int>::iterator it;
int main()
{
int T;
int n,m;
while(scanf("%d",&n)!=EOF)
{
ac.init();
for(int i=1;i<=n;i++)
{
scanf("%s",ss[i]);
ac.insert(ss[i],i);
}
ac.getFail();
scanf("%s",txt);
set<int> st;//本题中set并没有实际含义,只是上一题不能重复计数用到了,懒得删了= =
ac.find(txt,st);
for(int i=1;i<=n;i++)
{
if(!ac.cnt[i]) continue;
printf("%s: %d\n",ss[i],ac.cnt[i]);
}
}
return 0;
}