洛谷P3808 【模板】AC自动机(简单版)
题目描述
给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过。
输入输出格式
- 输入格式:
第一行一个n,表示模式串个数;
下面n行每行一个模式串;
下面一行一个文本串。 - 输出格式:
一个数表示答案
输入输出样例
- 输入样例1:
2
a
aa
aa - 输出样例1:
2
说明
subtask1[50pts]:∑length(模式串)<=10610^6106,length(文本串)<=10610^6106,n=1;
subtask2[50pts]:∑length(模式串)<=10610^6106,length(文本串)<=10610^6106;
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
//用∑length(模式串)作为maxn
const int maxn=1000005;
int n;
char s[maxn];
char ss[maxn];
class AC_automatic
{
public:
AC_automatic()
{
size=0;
memset(ch,0,sizeof(ch));
memset(val,0,sizeof(val));
memset(fail,0,sizeof(fail));
}
//首先建立trie树
inline void insert(char *s)
{
int u=0;
int len=(int)strlen(s);
for(int i=0;i<len;i++)
{
int c=s[i]-'a';
if(!ch[u][c])
ch[u][c]=++size;
u=ch[u][c];
}
//作为模式串结尾的标记,同时记录模式串出现次数
val[u]++;
}
void build()
{
for(int i=0;i<26;i++)
{
if(ch[0][i])
{
fail[ch[0][i]]=0;
qu.push(ch[0][i]);
}
}
//bfs找出所有节点的fail指针
while(!qu.empty())
{
int u=qu.front();
qu.pop();
for(int i=0;i<26;i++)
{
if(ch[u][i])
{
//与kmp类似,每次退回前一个点的fail指针的下一个点
fail[ch[u][i]]=ch[fail[u]][i];
qu.push(ch[u][i]);
}
else
//如果这个点不存在,就直接跳到前一个点的fail指针的下一个点上
ch[u][i]=ch[fail[u]][i];
}
}
}
int ask(char *s)
{
int len=strlen(s);
int now=0,ans=0;
for(int i=0;i<len;i++)
{
now=ch[now][s[i]-'a'];
for(int t=now;t&&~val[t];t=fail[t])
{
ans+=val[t];
//保证每个模式串只被计算一次
val[t]=-1;
}
}
return ans;
}
private:
int size;
int ch[maxn][26];
int val[maxn];
int fail[maxn];
queue<int> qu;
}ac;
int main()
{
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
ac.insert(s);
}
ac.build();
scanf("%s",ss);
printf("%d\n",ac.ask(ss));
return 0;
}