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

题目描述

有NN个由小写字母组成的模式串以及一个文本串TT。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串TT中出现的次数最多。

输入输出格式

输入格式:

 

输入含多组数据。

每组数据的第一行为一个正整数NN,表示共有NN个模式串,1 \leq N \leq 1501≤N≤150。

接下去NN行,每行一个长度小于等于7070的模式串。下一行是一个长度小于等于10^6106的文本串TT。

输入结束标志为N=0N=0。

 

输出格式:

 

对于每组数据,第一行输出模式串最多出现的次数,接下去若干行每行输出一个出现次数最多的模式串,按输入顺序排列。

 

输入输出样例

输入样例#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 <cmath>
#include <algorithm>
#include <cstring>
#define maxn 1000005
using namespace std;
struct trie{
	int e;  //是第几个模式串的尾字母 
	int fail;
	int next[26];
}tree[maxn];
struct T{
	string str;
	int x, i;  //str出现的次数,str是第几个输入的 
	bool operator<(const T& a)const{
		if(x != a.x)
			return x > a.x;
		else
			return i < a.i;
	}
}ans[155];
const int root = 0;
int cnt;
void clean(int x)  //清除节点信息 
{
	memset(tree[x].next, 0, sizeof(tree[x].next));
	tree[x].e = 0;
	tree[x].fail = 0;
}
void build(string str, int index) //建树 
{
	int now = root;
	for(int i = 0; i < str.size(); i ++){
		if(!tree[now].next[str[i]-'a']){
			tree[now].next[str[i]-'a'] = ++cnt;
			clean(cnt);  //清除新增节点的旧信息 
		}		
		now = tree[now].next[str[i]-'a'];					
	}
	tree[now].e = index;
}
void get_fail()
{
	queue<int> que;
	for(int i = 0; i < 26; i ++){
		if(tree[0].next[i]){
			tree[tree[0].next[i]].fail = 0;
			que.push(tree[0].next[i]);
		}		
	}
	while(!que.empty()){
		int now = que.front();
		que.pop();
		for(int i = 0; i < 26; i ++){
			if(tree[now].next[i]){
				tree[tree[now].next[i]].fail = tree[tree[now].fail].next[i];
				que.push(tree[now].next[i]);
			}
			else
				tree[now].next[i] = tree[tree[now].fail].next[i];			
		}
	}
}
void ac_query(string str) //匹配 
{
	int now = root;
	for(int i = 0; i < str.size(); i ++){
		now = tree[now].next[str[i]-'a'];
		int u = now;
		while(u){
			ans[tree[u].e].x ++;
			u = tree[u].fail;
		}
	}
}
int main()
{
	std::ios::sync_with_stdio(false);
	int n;
	while(cin >> n && n){
		cnt = 0;
		clean(cnt); //清除根节点旧信息 
		for(int i = 1; i <= n; i ++){
			string str;
			cin >> str;
			ans[i].str = str;
			ans[i].i = i;
			ans[i].x = 0;
			build(str, i);
		}
		get_fail();
		string s;
		cin >> s;
		ac_query(s);
		sort(ans + 1, ans + n + 1);
		int maxx = ans[1].x;
		cout << maxx << endl;
		for(int i = 1; i <= n; i ++){
			if(ans[i].x == maxx)
				cout << ans[i].str << endl;
			else
				break;
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值