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

91 篇文章 0 订阅
10 篇文章 0 订阅

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

题目链接:luogu P3796

题目大意

有一些字符串(互不相同),求哪些字符串在一个大字符串中出现的次数最多。

思路

如果没看过简单版的就点我看普通的。

这道题变了一下,从求有多少个字符串出现过,变成了求哪些字符串出现的次数最多。
那还记得我们之前要记录这个字符串是否出现过吗?

没错,我们就从这里改一下,就我们可以记录这个类型的字符串包含了哪几个,然后就每次发现这种字符串出现在大字符串一次,就给每一个字符串统计一次。

然后最后找最大值,就可以了。

不过好像也可以把相同的字符串合成一个,然后最后变回去就可以,好像内存空间还小一点。

代码

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

struct node {
	int num[151], fail;
	int son[31];
}tree[20001];
int n, size, now, thi, maxx[151], maxn, sum[151], noww, KK;
char c[1000001], every[151][71];

void getTrie(int nowww) {
	size = strlen(c);
	now = 0;
	
	for (int i = 0; i < size; i++) {
		thi = c[i] - 'a';
		if (!tree[now].son[thi]) tree[now].son[thi] = ++KK;
		now = tree[now].son[thi];
	}
	
	tree[now].num[++tree[now].num[0]] = nowww;//记录哪些字符串是同一个
}

void get_fail() {
	queue <int> q;
	
	for (int i = 0; i < 26; i++)
		if (tree[0].son[i]) {
			tree[tree[0].son[i]].fail = 0;
			q.push(tree[0].son[i]);
		}
	
	while (!q.empty()) {
		now = q.front();
		q.pop();
		
		for (int i = 0; i < 26; i++) {
			if (tree[now].son[i]) {
				tree[tree[now].son[i]].fail = tree[tree[now].fail].son[i];
				q.push(tree[now].son[i]);
			}
			else tree[now].son[i] = tree[tree[now].fail].son[i];
		}
	}
}

void get_AC() {
	size = strlen(c);
	now = 0;
	
	for (int i = 0; i < size; i++) {
		thi = c[i] - 'a';
		now = tree[now].son[thi];
		
		noww = now;
		while (noww) {
			for (int j = 1; j <= tree[noww].num[0]; j++) {
				if (tree[noww].num[j] == 0) continue;
				sum[tree[noww].num[j]]++;
				if (sum[tree[noww].num[j]] > maxn) {//新的最大
					maxn = sum[tree[noww].num[j]];
					maxx[0] = 1;
					maxx[1] = tree[noww].num[j];
				}
				else if (sum[tree[noww].num[j]] == maxn) {//原来的最大
					maxx[++maxx[0]] = tree[noww].num[j];
				}
			}
			
			noww = tree[noww].fail;
		}
	}
}

bool cmp(int x, int y) {//把找到的字符串的位置按照输入的位置排序
	return x < y;
}

int main() {
	scanf("%d", &n);
	while (n) {
		memset(tree, 0, sizeof(tree));
		memset(sum, 0, sizeof(sum));
		memset(maxx, 0, sizeof(maxx));
		memset(every, 0, sizeof(every));
		KK = 0;
		maxn = 0;
		
		for (int i = 1; i <= n; i++) {
			scanf("%s", &c);
 			size = strlen(c);
			for (int j = 0; j < size; j++)
				every[i][j] = c[j];
			getTrie(i);
		}
		
		get_fail();
		
		scanf("%s", &c);
		get_AC();
		
		printf("%d\n", maxn);
		sort(maxx + 1, maxx + maxx[0] + 1, cmp);
		for (int i = 1; i <= maxx[0]; i++) {
			size = strlen(every[maxx[i]]);
			for (int j = 0; j < size; j++)
				printf("%c", every[maxx[i]][j]);
			printf("\n");
		}
		
		scanf("%d", &n);
	}
	
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值