【ybt高效进阶2-5-5】【luogu P2444】病毒代码 / 病毒

病毒代码 / 病毒

题目链接:ybt高效进阶2-5-5 / luogu P2444

题目大意

有一些 01 串,然后问你是否存在一个长度无限的 01 串,使得这个串中的任何子串都不是前面给出的 01 串的其中一个。

思路

那首先看到这道题要判断一些串是否在一个串中出现,会想到 AC 自动机。

那我们看到说是不能出现一些串,那我们就用这些串建 Trie 树,然后把这些串的位置都打一个标记。

那你再想,你建 fail 边之后,它连向的点一定是它的子串。
那如果它的子串中含有病毒串,那它也一定会含有病毒串。
那你就在建 fail 边的时候,处理一下。
(可以这样是因为你处理的时候这个点的 fail 边已经建好了)

然后你考虑怎么弄,你可以想到,如果你一直跑 Trie 树,如果你能找到一个环,这个环中没有被标记为含有病毒串的点,那你就可以弄出无限长度的字符串。

为什么会有环呢,你会想到建 fail 边的时候如果没有儿子就直接把儿子当成 fail 边指向的点,那这么弄的话就可能会有环,有环的话也是可行的。

代码

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

struct Trie {
	int son[2], fail;
	bool cnt;
}tree[30001];
int n, size;
char c[30001];
int tot;
queue <int> q;
bool in[30001], gone[30001];

void insert() {//AC 自动机插入
	int now = 0;
	
	for (int i = 0; i < size; i++) {
		int go = c[i] - '0';
		if (!tree[now].son[go]) tree[now].son[go] = ++tot;
		now = tree[now].son[go];
	}
	
	tree[now].cnt = 1;//标记不能出现这个串
}

void get_fail() {//AC 自动机建 fail 边
	for (int i = 0; i <= 1; i++)
		if (tree[0].son[i]) {
			q.push(tree[0].son[i]);
			tree[tree[0].son[i]].fail = 0;
		}
	
	while (!q.empty()) {
		int now = q.front();
		q.pop();
		
		for (int i = 0; i <= 1; i++)
			if (tree[now].son[i]) {
				q.push(tree[now].son[i]);
				tree[tree[now].son[i]].fail = tree[tree[now].fail].son[i];
			}
			else tree[now].son[i] = tree[tree[now].fail].son[i];
		
		tree[now].cnt |= tree[tree[now].fail].cnt;
	}
}

bool find(int now) {//找是否有环
	if (in[now]) {
		printf("TAK");
		return 1;
	}
	
	if (gone[now]) return 0;//小小的优化
	
	in[now] = 1;
	gone[now] = 1;
	
	for (int i = 0; i <= 1; i++)
		if (!tree[tree[now].son[i]].cnt) {
			if (find(tree[now].son[i])) return 1;
		}
	
	in[now] = 0;
	
	return 0;
}

int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> c;
		size = strlen(c);
		insert();
	}
	
	get_fail();
	
	if (!find(0)) printf("NIE");
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值