题意:给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过。
题解:ac自动机+加一点点优化,否则过不了,具体见代码,就一行代码。
传送门
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=1000005;//字符串长度
struct ac_auto{
const static int letter_size = 26;
const static int trie_size = (1e6+50);
int tot , root ,fail[trie_size],end[trie_size],next[trie_size][letter_size];
int newnode()
{
end[tot]=0;
memset(next[tot],-1,sizeof next[tot]);
return tot++;
}
void init()
{
tot=0;
root=newnode();
}
int getidx(char x){
return x-'a';
}
void insert(char ss[])//建字典树
{
int len=strlen(ss);
int now=root;
for(int i=0;i<len;i++)
{
int id=getidx(ss[i]);
if(next[now][id]==-1)
{
next[now][id]=newnode();
}
now=next[now][id];
}
end[now]++;
}
void getfail()//求出fail指针
{
queue<int>q;
fail[root]=root;
for(int i=0;i<letter_size;i++)//初始化
{
if(next[root][i]!=-1)
{
fail[next[root][i]]=root;
q.push(next[root][i]);
}
else
{
next[root][i]=root;
}
}
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<letter_size;i++)
{
if(next[now][i]!=-1)
{
fail[next[now][i]]=next[fail[now]][i];//相当于next[now][i]新加入一个分支,用fail来维护这条分支的链接。
q.push(next[now][i]);
}
else next[now][i]=next[fail[now]][i];//这里因为next[now][i]没有后继,直接指向另一条分支
}
}
}
int match(char ss[])
{
int sum=0,now=root;
int len=strlen(ss);
for(int i=0;i<len;i++)
{
int id=getidx(ss[i]);
int tmp=now=next[now][id];
while(tmp)
{
//不优化
//if(end[tmp]!=-1)
//res += end[tmp];
//优化
if(end[tmp]==-1)break;//移边删除小优化
sum+=end[tmp];
end[tmp]=-1;
tmp=fail[tmp];
}
}
return sum;
}
};
ac_auto ac;
char ss[maxn];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
ac.init();
for(int i=0;i<n;i++)
{
scanf("%s",ss);
ac.insert(ss);
}
ac.getfail();
scanf("%s",ss);
printf("%d\n",ac.match(ss));
}
return 0;
}