您的位置:首页 > 其它

BZOJ 2938: [Poi2000]病毒 AC自动机

2016-12-28 18:41 344 查看
Description

二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。

示例:

例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。

任务:

请写一个程序:

l 读入病毒代码;

l 判断是否存在一个无限长的安全代码;

l 将结果输出

Input

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

Output

你应在在文本文件WIN.OUT的第一行输出一个单词:

l TAK——假如存在这样的代码;

l NIE——如果不存在。

解析:

这题一看有好多个病毒片段就知道应该是AC自动机了,但是怎么样才能判断出是否存在无限长的安全码呢,不难发现,当且仅当构建出的AC自动机上有经过根节点且不含任何危险代码的环,用一个dfs判环就好了

【注】:代码中判环中的son[i]->match=true;是为了让下一次搜到这直接跳过,否则超时

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<iostream>
#include<iomanip>
#include<algorithm>
using namespace std;
struct Trie
{
Trie *son[2],*fail;
bool match;
bool use;
Trie()
{
memset(son,0,sizeof(son));
fail=NULL;
match=use=0;
}
bool pc()
{
use=true;
for(int i=0;i<=1;i++)
{
if(son[i]->use) return true;
if(son[i]->match) continue;
son[i]->match=true;
if(son[i]->pc()) return true;
}
use=false;
return false;
}
}*root=new Trie();
void my_insert(char *s)
{
Trie *o=root;
while(*s)
{
int x=(*s)-'0';
if(!o->son[x]) o->son[x]=new Trie();
o=o->son[x];
s++;
}
o->match=true;
}
void bfs()
{
static Trie* dui[1200000];
int top=1,my_final=1;
for(int i=0;i<2;i++)
{
if(root->son[i])
{
dui[my_final++]=root->son[i];
root->son[i]->fail=root;
}
else root->son[i]=root;
}
while(top<my_final)
{
Trie *o=dui[top];
for(int i=0;i<2;i++)
{
if(o->son[i])
{
dui[my_final++]=o->son[i];
o->son[i]->fail=o->fail->son[i];
}
else o->son[i]=o->fail->son[i];
}
top++;
Trie *mid=o;
while(mid!=root && !mid->match) mid=mid->fail;
o->match=mid->match;
}
}
char s[1000101];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
my_insert(s);
}
bfs();
if(root->pc()) printf("TAK\n");
else      printf("NIE\n");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  AC自动机 bzoj