题意
二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例:
例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。
任务:
请写一个程序:
1.在文本文件WIR.IN中读入病毒代码;
2.判断是否存在一个无限长的安全代码;
3.将结果输出到文件WIR.OUT中。
输入格式:
在文本文件WIR.IN的第一行包括一个整数n(nle 2000)(n≤2000),表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过30000。
输出格式:
在文本文件WIR.OUT的第一行输出一个单词:
TAK——假如存在这样的代码;
NIE——如果不存在。
题解
很容易想到将病毒串放入Trie树,补全后,如果能找到一个不经过任何串末尾的环就有一个安全代码
考虑如何找环,本来想的是拓扑排序,不过就算找出一个环,也很难判断是否含末尾点
然后又不小心听到了dfs,想了想很有道理,于是写出了如下东西
结果WA了一片,第一个样例就错了.....
那考虑为什么会这样,按照上面的写法,下个点访问过一定是环吗?
不一定,补全后就是一个有向图,所以我们拿有向图举例
如果先从1遍历完,然后回到1,再从2到1时,就会判断是环
然而不是,因为这是有向图,如果是无向图的话确实有环。
那么考虑怎么更正
在回溯时删除vis标记即可,这样能否找到环?
答案是确定的,因为会遍历到每个路径
#include<bits/stdc++.h>
using namespace std;
const int maxn=30005;
int n,num;
int go[maxn][2],fail[maxn];
char s[maxn];
bool end[maxn],vis[maxn];
void extend(int len){
int now=0;
for(int i=0;i<len;i++){
int c=s[i]-'0';
if(!go[now][c]) go[now][c]=++num;
now=go[now][c];
}
end[now]=true;
}
queue<int> q;
void get_fail(){
if(go[0][0]) q.push(go[0][0]);
if(go[0][1]) q.push(go[0][1]);
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=0;i<=1;i++)
if(!go[now][i]) go[now][i]=go[fail[now]][i];
else {
fail[go[now][i]]=go[fail[now]][i];
end[go[now][i]]|=end[fail[go[now][i]]];
q.push(go[now][i]);
}
}
}
bool dfs(int now){
if(end[now]) return false;
for(int i=0;i<=1;i++)
if(!vis[go[now][i]]){
vis[go[now][i]]=true;
if(dfs(go[now][i])) return true;
vis[go[now][i]]=false;
}
else if(!end[go[now][i]]) return true;
return false;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%s",s),extend(strlen(s));
get_fail();
vis[0]=true;
printf("%s",dfs(0) ? "TAK" : "NIE");
}
然后在bzoj上就T了,一看评论才知道。
需要两个bool数组,vis记录是否访问过,访问过就不访问,达到剪枝目的,sta记录是否在当前路径
不会错吗?考虑如果下一个要遍历的点在我们要的环中,但它访问过了,当第一次访问它时,它就应该找出环,所以不可能。
luogu真的有点小水
#include<bits/stdc++.h> using namespace std; const int maxn=30005; int n,num; int go[maxn][2],fail[maxn]; char s[maxn]; bool end[maxn],vis[maxn],sta[maxn];//是否访问过,是否在当前路径 void extend(int len){ int now=0; for(int i=0;i<len;i++){ int c=s[i]-'0'; if(!go[now][c]) go[now][c]=++num; now=go[now][c]; } end[now]=true; } queue<int> q; void get_fail(){ if(go[0][0]) q.push(go[0][0]); if(go[0][1]) q.push(go[0][1]); while(!q.empty()){ int now=q.front(); q.pop(); for(int i=0;i<=1;i++) if(!go[now][i]) go[now][i]=go[fail[now]][i]; else { fail[go[now][i]]=go[fail[now]][i]; end[go[now][i]]|=end[fail[go[now][i]]]; q.push(go[now][i]); } } } bool dfs(int now){ if(end[now]) return false; for(int i=0;i<=1;i++) if(!vis[go[now][i]]){ sta[go[now][i]]=true; vis[go[now][i]]=true; if(dfs(go[now][i])) return true; sta[go[now][i]]=false; } else if(sta[go[now][i]]&&!end[go[now][i]]) return true; return false; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%s",s),extend(strlen(s)); get_fail(); sta[0]=vis[0]=true; printf("%s",dfs(0) ? "TAK" : "NIE"); }