【POI 2000】病毒

传送门


Problem

给出 n n n 01 01 01 串,我们称这些串为病毒代码,询问是否存在一个无限长的 01 01 01 串,使得任何一个病毒代码都不是它的子串。若存在输出 TAK,否则输出 NIE

例如,如果 { 011 , 11 , 00000 } \{011, 11, 00000\} {011,11,00000} 为病毒代码段,那么一个可能的无限长的 01 01 01 串就是 010101 ⋯ 010101\cdots 010101;如果 { 01 , 11 , 000000 } \{01, 11, 000000\} {01,11,000000} 为病毒代码段,那么就不存在一个无限长的 01 01 01 串。


Solution

我们先把这 n n n 个串的AC自动机建出来。

由于病毒代码不能是它的字串,所以我们在匹配的时候就不能走到尾结点(走到尾结点就相当于匹配了一个病毒代码)。

又因为这是一个无限长的 01 01 01 串,那么在AC自动机上应该存在一个没有尾结点的环使它一直在环上跳。

因此我们打任务就是判断建好的AC自动机上是否有没有尾结点的环。

于是,就用 d f s dfs dfs 遍历AC自动机,再用一个栈来记录当前链的点,如果 d f s dfs dfs 到一个已经在栈里的点,就说明有环。


Code

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 30005
using namespace std;
int tot=0;
char S[N];
struct Trie{
	int son[2];
	int fail,end;
}a[N];
bool vis[N],insta[N];
void insert(){
	int i,p=0;
	int l=strlen(S);
	for(i=0;i<l;++i){
		int x=S[i]-'0';
		if(!a[p].son[x])
			a[p].son[x]=++tot;
		p=a[p].son[x];
	}
	a[p].end=1;
}
void Get_fail(){
	int x,i;
	queue<int>Q;
	for(i=0;i<2;++i){
		if(a[0].son[i]){
			a[a[0].son[i]].fail=0;
			Q.push(a[0].son[i]);
		}
	}
	while(!Q.empty()){
		x=Q.front();Q.pop();
		for(i=0;i<2;++i){
			if(a[x].son[i]){
				Q.push(a[x].son[i]);
				a[a[x].son[i]].fail=a[a[x].fail].son[i];
				if(a[a[x].fail].end)  a[x].end=1;
			}
			else  a[x].son[i]=a[a[x].fail].son[i];
		}
	}
}
void dfs(int x){
	if(a[x].end)  return;
	if(insta[x])  puts("TAK"),exit(0);
	if(vis[x])  return;
	vis[x]=insta[x]=true;
	for(int i=0;i<2;++i)
		if(a[x].son[i])  dfs(a[x].son[i]);
	insta[x]=false;
}
int main(){
	int n,i;
	scanf("%d",&n);
	for(i=1;i<=n;++i){
		scanf("%s",S);
		insert();
	}
	Get_fail(),dfs(0);
	puts("NIE");
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值