BZOJ 2938: [Poi2000]病毒
2938: [Poi2000]病毒
Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 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
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

浙公网安备 33010602011771号