版权声明:本文为博主原创文章,未经博主允许不得转载,除非先点了赞。 https://blog.csdn.net/A_Bright_CH/article/details/83145280
题目
二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例:
例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。
任务:
请写一个程序:
1.在文本文件WIR.IN中读入病毒代码;
2.判断是否存在一个无限长的安全代码;
3.将结果输出到文件WIR.OUT中。
题解
AC自动机+DFS判环
给病毒代码建字典树,并建立fail指针。
要求一个无限长的安全代码实际上就是要在AC自动机上找到一个不包含结尾标记的环,当然,如果一个fail指针指向的节点有结尾标记,那么这个点也不能选,因为到了这个节点相当于到了那个节点,也就是说它的后缀是病毒代码。
为了方便操作,我们可以把没有孩子的尾节点直接接上fail。
接下来就是一个dfs判断是否有环,注意不能走有标记的节点。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2010,maxl=30010;
int n;
char s[maxl];
struct tree{int fail,son[2];bool c;}tr[maxl];int tot=1,root=1;
void insert()
{
int x=root;
int len=strlen(s+1);
for(int i=1;i<=len;i++)
{
int k=s[i]-'0';
if(!tr[x].son[k]) tr[x].son[k]=++tot;
x=tr[x].son[k];
}
tr[x].c=true;
}
int head,tail,q[maxl];
void makefail()
{
head=0,tail=1;q[0]=root;
while(head<tail)
{
int x=q[head++];
for(int k=0;k<=1;k++)
{
int y=tr[x].son[k];
if(!y)
{
tr[x].son[k]=tr[tr[x].fail].son[k];
continue;
}
int p=tr[x].fail;
while(!tr[p].son[k]) p=tr[p].fail;
tr[y].fail=tr[p].son[k];
tr[y].c|=tr[tr[y].fail].c;
q[tail++]=y;
}
}
}
bool vis[maxl];
void dfs(int x)
{
vis[x]=true;
for(int k=0;k<=1;k++)
{
int y=tr[x].son[k];
if(vis[y]==true)
{
puts("TAK");
exit(0);
}
if(y && !tr[y].c) dfs(y);
}
vis[x]=false;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
insert();
}
for(int i=0;i<=1;i++) tr[0].son[i]=1;
makefail();
dfs(root);
puts("NIE");
return 0;
}