AC 自动机(简单版)
题目描述
给定
n
n
n 个模式串
s
i
s_i
si 和一个文本串
t
t
t,求有多少个不同的模式串在文本串里出现过。
两个模式串不同当且仅当他们编号不同。
输入格式
第一行是一个整数,表示模式串的个数
n
n
n。
第
2
2
2 到第
(
n
+
1
)
(n + 1)
(n+1) 行,每行一个字符串,第
(
i
+
1
)
(i + 1)
(i+1) 行的字符串表示编号为
i
i
i 的模式串
s
i
s_i
si。
最后一行是一个字符串,表示文本串
t
t
t。
输出格式
输出一行一个整数表示答案。
样例 #1
样例输入 #1
3
a
aa
aa
aaa
样例输出 #1
3
样例 #2
样例输入 #2
4
a
ab
ac
abc
abcd
样例输出 #2
3
样例 #3
样例输入 #3
2
a
aa
aa
样例输出 #3
2
提示
样例 1 解释
s 2 s_2 s2 与 s 3 s_3 s3 编号(下标)不同,因此各自对答案产生了一次贡献。
样例 2 解释
s
1
s_1
s1,
s
2
s_2
s2,
s
4
s_4
s4 都在串 abcd
里出现过。
数据规模与约定
- 对于 50 % 50\% 50% 的数据,保证 n = 1 n = 1 n=1。
- 对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 1 0 6 1 \leq n \leq 10^6 1≤n≤106, 1 ≤ ∣ t ∣ ≤ 1 0 6 1 \leq |t| \leq 10^6 1≤∣t∣≤106, 1 ≤ ∑ i = 1 n ∣ s i ∣ ≤ 1 0 6 1 \leq \sum\limits_{i = 1}^n |s_i| \leq 10^6 1≤i=1∑n∣si∣≤106。 s i , t s_i, t si,t 中仅包含小写字母。
代码实现
#include<iostream>
using namespace std;
#define MAXSIZE 1000000
int node[MAXSIZE+5][26];
int fail[MAXSIZE+5]={0};
int val[MAXSIZE+5]={0};
int que[MAXSIZE+5]={0};
char s[MAXSIZE+5];
int cnt=1,root=1;
int getNewNode()
{
return ++cnt;
}
void insert(const char*s)
{
int p=root;
for(int i=0;s[i];i++)
{
int ind=s[i]-'a';
if(!node[p][ind])node[p][ind]=getNewNode();
p=node[p][ind];
}
val[p]+=1;
return ;
}
void build_ac()
{
int head=0,tail=0,p;
que[tail++]=root;
while(head<tail)
{
int cur=que[head++];
for(int i=0;i<26;i++)
{
if(!node[cur][i])
{
if(!fail[cur])node[cur][i]=root;
else node[cur][i]=node[fail[cur]][i];
continue;
}
p=fail[cur];
if(p==0)p=root;
else p=node[p][i];
fail[node[cur][i]]=p;
que[tail++]=node[cur][i];
}
}
return ;
}
int find_all(const char*s)
{
int p=root,q,ans=0;
for(int i=0;s[i];i++)
{
int ind=s[i]-'a';
p=node[p][ind];
q=p;
while(q&&val[q]!=-1)
{
ans+=val[q];
val[q]=-1;
q=fail[q];
}
}
return ans;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
scanf("%s\n",s);
insert(s);
}
build_ac();
scanf("%s",s);
cout<<find_all(s);
return 0;
}
AC 自动机(简单版 II)
题目描述
有 N N N 个由小写字母组成的模式串以及一个文本串 T T T。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串 T T T 中出现的次数最多。
输入格式
输入含多组数据。保证输入数据不超过 50 50 50 组。
每组数据的第一行为一个正整数 N N N,表示共有 N N N 个模式串, 1 ≤ N ≤ 150 1 \leq N \leq 150 1≤N≤150。
接下去 N N N 行,每行一个长度小于等于 70 70 70 的模式串。下一行是一个长度小于等于 1 0 6 10^6 106 的文本串 T T T。保证不存在两个相同的模式串。
输入结束标志为 N = 0 N=0 N=0。
输出格式
对于每组数据,第一行输出模式串最多出现的次数,接下去若干行每行输出一个出现次数最多的模式串,按输入顺序排列。
样例 #1
样例输入 #1
2
aba
bab
ababababac
6
beta
alpha
haha
delta
dede
tata
dedeltalphahahahototatalpha
0
样例输出 #1
4
aba
2
alpha
haha
代码实现
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
typedef struct Node{
int id;
Node*fail;
Node*next[26];
}Node;
int node_cnt=0;
char s[1000005],t[200][75];
int tcnt[200];
Node*getNewNode()
{
Node*p=(Node*)malloc(sizeof(Node));
p->id=-1;
p->fail=NULL;
for(int i=0;i<26;i++)p->next[i]=NULL;
return p;
}
void insert(Node*root,const char*s,int k)
{
Node*p=root;
for(int i=0;s[i];i++)
{
int ind=s[i]-'a';
if(!p->next[ind])p->next[ind]=getNewNode();
p=p->next[ind];
}
p->id=k;
strcpy(t[k],s);
return ;
}
void build_ac(Node*root)
{
queue<Node*>q;
q.push(root);
while(!q.empty())
{
Node*cur=q.front();
q.pop();
for(int i=0;i<26;i++)
{
if(!cur->next[i])
{
if(!cur->fail)cur->next[i]=root;
else cur->next[i]=cur->fail->next[i];
continue;
}
Node*p=cur->fail;
if(!p)p=root;
else p=p->next[i];
cur->next[i]->fail=p;
q.push(cur->next[i]);
}
}
return ;
}
void find_ac(Node*root,const char*s)
{
Node*p=root,*q;
for(int i=0;s[i];i++)
{
int ind=s[i]-'a';
p=p->next[ind];
q=p;
while(q)
{
if(q->id!=-1)tcnt[q->id]++;
q=q->fail;
}
}
return ;
}
void Init()
{
node_cnt=0;
memset(t,0,sizeof(t));
memset(tcnt,0,sizeof(tcnt));
return ;
}
void solve(int n)
{
Init();
Node*root=getNewNode();
for(int i=0;i<n;i++)
{
scanf("%s",s);
insert(root,s,i);
}
build_ac(root);
scanf("%s",s);
find_ac(root,s);
int ans=0;
for(int i=0;i<n;i++)
{
if(ans<tcnt[i])ans=tcnt[i];
}
printf("%d\n",ans);
for(int i=0;i<n;i++)
{
if(ans==tcnt[i])printf("%s\n",t[i]);
}
return ;
}
int main()
{
int n;
while(~scanf("%d",&n)!=EOF)
{
if(n==0)break;
solve(n);
}
return 0;
}