【洛谷P3796】【模板】AC自动机(加强版)

题目

题目链接:https://www.luogu.com.cn/problem/P3796
\(N\) 个由小写字母组成的模式串以及一个文本串 \(T\)。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串 \(T\) 中出现的次数最多。

思路

直接建立 AC 自动机,询问时暴力跳 fail 并记录每个串被走到的次数即可。
注意为了求出每个串出现次数就不用标记每个点是否被走过了。
时间复杂度 \(O(|S|\times |T|)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=1000010,M=80,K=160;
int n,ans,cnt[K];
char ch[K][M],s[N];

struct ACA
{
	int tot,c[M*K][27],fail[M*K],pos[M*K];
	
	void clr()
	{
		memset(c,0,sizeof(c));
		memset(fail,0,sizeof(fail));
		memset(pos,0,sizeof(pos));
		tot=0;
	}
	
	void ins(char *s,int k)
	{
		int len=strlen(s+1),p=0;
		for (int i=1;i<=len;i++)
		{
			int id=s[i]-'a'+1;
			if (!c[p][id]) c[p][id]=++tot;
			p=c[p][id];
		}
		pos[p]=k;
	}
	
	void build()
	{
		queue<int> q;
		for (int i=1;i<=26;i++)
			if (c[0][i]) q.push(c[0][i]);
		while (q.size())
		{
			int u=q.front();
			q.pop();
			for (int i=1;i<=26;i++)
				if (c[u][i]) fail[c[u][i]]=c[fail[u]][i],q.push(c[u][i]);
					else c[u][i]=c[fail[u]][i];
		}
	}
	
	void query(char *s)
	{
		int len=strlen(s+1),p=0;
		for (int i=1;i<=len;i++)
		{
			int id=s[i]-'a'+1;
			p=c[p][id];
			for (int j=p;j;j=fail[j])
				cnt[pos[j]]++;
		}
	}
}AC;

int main()
{
	//freopen("data.txt","r",stdin);
	while (scanf("%d",&n))
	{
		if (!n) return 0;
		memset(cnt,0,sizeof(cnt));
		AC.clr();
		for (int i=1;i<=n;i++)
		{
			scanf("%s",ch[i]+1);
			AC.ins(ch[i],i);
		}
		scanf("%s",s+1);
		AC.build();	AC.query(s);
		ans=0;
		for (int i=1;i<=n;i++)
			if (cnt[i]>ans) ans=cnt[i];
		printf("%d\n",ans);
		for (int i=1;i<=n;i++)
			if (cnt[i]==ans) printf("%s\n",ch[i]+1);
	}
}
posted @ 2020-09-03 17:49  stoorz  阅读(117)  评论(0编辑  收藏  举报