学习AC自动机:强势 图解 AC自动机(保证您一次就能学会!)_繁凡さん的博客-CSDN博客_ac自动机
题目描述
给定 nn 个模式串 s_isi 和一个文本串 tt,求有多少个不同的模式串在文本串里出现过。
两个模式串不同当且仅当他们编号不同。
输入格式
第一行是一个整数,表示模式串的个数 nn。
第 22 到第 (n + 1)(n+1) 行,每行一个字符串,第 (i + 1)(i+1) 行的字符串表示编号为 ii 的模式串 s_isi。
最后一行是一个字符串,表示文本串 tt。
输出格式
输出一行一个整数表示答案。
输入输出样例
输入 #1复制
3 a aa aa aaa
输出 #1复制
3
输入 #2复制
4 a ab ac abc abcd
输出 #2复制
3
输入 #3复制
2 a aa aa
输出 #3复制
2
说明/提示
样例 1 解释
s_2s2 与 s_3s3 编号(下标)不同,因此各自对答案产生了一次贡献。
样例 2 解释
s_1s1,s_2s2,s_4s4 都在串 abcd
里出现过。
数据规模与约定
- 对于 50\%50% 的数据,保证 n = 1n=1。
- 对于 100\%100% 的数据,保证 1 \leq n \leq 10^61≤n≤106,1 \leq |t| \leq 10^61≤∣t∣≤106,1 \leq \sum\limits_{i = 1}^n |s_i| \leq 10^61≤i=1∑n∣si∣≤106。s_i, tsi,t 中仅包含小写字母。
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=1e6+5;
struct ACautomatic{
int tire[N][26];
int tail[N];
int fail[N];
int cnt;
void init()
{
cnt=0;
memset(tail,0,sizeof(tail));
memset(tire,0,sizeof(tire));
}
void insert(char *s)
{
int len=strlen(s);
int p=0;
for(int i=0;i<len;i++)
{
if(tire[p][s[i]-'a']==0)tire[p][s[i]-'a']=++cnt;
p=tire[p][s[i]-'a'];
}
tail[p]++;
}
void build()
{
memset(fail,0,sizeof(fail));
queue<int> que;
for(int i=0;i<26;i++)if(tire[0][i])que.push(tire[0][i]);
while(!que.empty())
{
int v=que.front();que.pop();
for(int i=0;i<26;i++)
{
if(tire[v][i])
{
fail[tire[v][i]]=tire[fail[v]][i];
que.push(tire[v][i]);
}
else tire[v][i]=tire[fail[v]][i];
}
}
}
void search(int &ans,char *s)
{
ans=0;
int len=strlen(s);
int p=0;
for(int i=0;i<len;i++)
{
p=tire[p][s[i]-'a'];
for(int j=p;j&&~tail[j];j=fail[j])ans+=tail[j],tail[j]=-1;
}
}
};
int main()
{
int n;
cin>>n;
char str[1000005];
ACautomatic AC;
AC.init();
while(n--)
{
scanf("%s",str);
AC.insert(str);
}
AC.build();
scanf("%s",str);
int ans;
AC.search(ans,str);
printf("%d",ans);
}