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