AC自动机模板题

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 1n106 1 ≤ ∣ t ∣ ≤ 1 0 6 1 \leq |t| \leq 10^6 1t106 1 ≤ ∑ i = 1 n ∣ s i ∣ ≤ 1 0 6 1 \leq \sum\limits_{i = 1}^n |s_i| \leq 10^6 1i=1nsi106 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 1N150

接下去 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;
 } 
  • 22
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值