BZOJ 2938: [Poi2000]病毒

2938: [Poi2000]病毒

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 727  Solved: 373
[Submit][Status][Discuss]

Description

二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例:
例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。
任务:
请写一个程序:
l         读入病毒代码;
l         判断是否存在一个无限长的安全代码;
l         将结果输出

Input

 
第一行包括一个整数n,表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过30000。

Output

你应在在文本文件WIN.OUT的第一行输出一个单词:
l         TAK——假如存在这样的代码;
l         NIE——如果不存在。

Sample Input

3
01
11
00000

Sample Output

NIE

HINT

 

Source

分析:

如果存在一个无限长的串那就是这个串在AC自动机上不断匹配但是永远无法完全匹配...

也就是说我们在AC自动机上不断游走,避开坏点,找到一个环,使得其不断匹配,这样就可以得到一个无限长的串...

所谓坏点就是可以完全匹配的点,一个字符串的最后一个点是坏点,然后如果一个点的fail指针指向坏点那么这个点也是坏点...

一个简单的方法就是我们不用不断用fail指针去游走而是先把fail指针的儿子建到当前节点的空儿子节点上...

代码:

先放一个错误的但是可以AC的代码...第50行那个地方是错误的...因为每个点的nxt只有01,这题的数据特殊,所以根节点没有空儿子,不会出现问题,但是其他题目就会RE...

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;

const int maxm=30000+5;

int n,tot,head,tail,q[maxm],dfn[maxm],vis[maxm],dan[maxm];

char s[maxm];

struct trie{
	int fail,nxt[2];
}tr[maxm];

inline void insert(char *s){
	int p=0,len=strlen(s);
	for(int i=0;i<len;i++){
		if(!tr[p].nxt[s[i]-'0'])
			tr[p].nxt[s[i]-'0']=++tot;
		p=tr[p].nxt[s[i]-'0'];tr[p].fail=-1;
	}
	dan[p]=1;
}

inline void buildACM(void){
	head=0,tail=0;q[0]=0;
	while(head<=tail){
		int id=q[head++],p=-1;
		for(int i=0;i<=1;i++){
			if(tr[id].nxt[i]){
				if(id){
					p=tr[id].fail;
					while(p!=-1){
						if(tr[p].nxt[i]){
							tr[tr[id].nxt[i]].fail=tr[p].nxt[i];
							break;
						}
						p=tr[p].fail;
					}
					if(p==-1) tr[tr[id].nxt[i]].fail=0;
				}
				else
					tr[tr[id].nxt[i]].fail=0;
				dan[tr[id].nxt[i]]|=dan[tr[tr[id].nxt[i]].fail];
				q[++tail]=tr[id].nxt[i];
			}
			else
				tr[id].nxt[i]=tr[tr[id].fail].nxt[i];
		}
	}
}

inline bool dfs(int root){
	dfn[root]=1;
	for(int i=0;i<=1;i++)
		if(tr[root].nxt[i]){
			if(dfn[tr[root].nxt[i]])
				return true;
			if(dan[tr[root].nxt[i]]||vis[tr[root].nxt[i]])
				continue;
			vis[tr[root].nxt[i]]=1;
			if(dfs(tr[root].nxt[i]))
				return true;
		}
	dfn[root]=0;
	return false;
}

signed main(void){
	scanf("%d",&n);tr[0].fail=-1;
	for(int i=1;i<=n;i++)
		scanf("%s",s),insert(s);
	buildACM();
	if(dfs(0))
		puts("TAK");
	else
		puts("NIE");
    return 0;
}

 接下来是正确的...

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;

const int maxm=30000+5;

int n,tot,head,tail,q[maxm],dfn[maxm],vis[maxm],dan[maxm];

char s[maxm];

struct trie{
	int fail,nxt[2];
}tr[maxm];

inline void insert(char *s){
	int p=0,len=strlen(s);
	for(int i=0;i<len;i++){
		if(!tr[p].nxt[s[i]-'0'])
			tr[p].nxt[s[i]-'0']=++tot;
		p=tr[p].nxt[s[i]-'0'];tr[p].fail=-1;
	}
	dan[p]=1;
}

inline void buildACM(void){
	head=0,tail=0;q[0]=0;
	while(head<=tail){
		int id=q[head++],p=-1;
		for(int i=0;i<=1;i++){
			if(tr[id].nxt[i]){
				if(id){
					p=tr[id].fail;
					while(p!=-1){
						if(tr[p].nxt[i]){
							tr[tr[id].nxt[i]].fail=tr[p].nxt[i];
							break;
						}
						p=tr[p].fail;
					}
					if(p==-1) tr[tr[id].nxt[i]].fail=0;
				}
				else
					tr[tr[id].nxt[i]].fail=0;
				dan[tr[id].nxt[i]]|=dan[tr[tr[id].nxt[i]].fail];
				q[++tail]=tr[id].nxt[i];
			}
			else if(id) 
				tr[id].nxt[i]=tr[tr[id].fail].nxt[i];
		}
	}
}

inline bool dfs(int root){
	dfn[root]=1;
	for(int i=0;i<=1;i++)
		if(tr[root].nxt[i]){
			if(dfn[tr[root].nxt[i]])
				return true;
			if(dan[tr[root].nxt[i]]||vis[tr[root].nxt[i]])
				continue;
			vis[tr[root].nxt[i]]=1;
			if(dfs(tr[root].nxt[i]))
				return true;
		}
	dfn[root]=0;
	return false;
}

signed main(void){
	scanf("%d",&n);tr[0].fail=-1;
	for(int i=1;i<=n;i++)
		scanf("%s",s),insert(s);
	buildACM();
	if(dfs(0))
		puts("TAK");
	else
		puts("NIE");
    return 0;
}

  

 


By NeighThorn

posted @ 2017-02-15 20:54  NeighThorn  阅读(547)  评论(0编辑  收藏  举报