洛谷传送门
BZOJ传送门
题目描述
二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例:
例如如果 { 011 , 11 , 00000 } \{011, 11, 00000\} {011,11,00000}为病毒代码段,那么一个可能的无限长安全代码就是 010101 … 010101… 010101…。如果 { 01 , 11 , 000000 } \{01, 11, 000000\} {01,11,000000}为病毒代码段,那么就不存在一个无限长的安全代码。
任务:
请写一个程序:
1.在文本文件WIR.IN中读入病毒代码;
2.判断是否存在一个无限长的安全代码;
3.将结果输出到文件WIR.OUT中。
输入输出格式
输入格式:
在文本文件WIR.IN的第一行包括一个整数 n ( n ≤ 2000 ) n(n\le 2000) n(n≤2000),表示病毒代码段的数目。以下的 n n n行每一行都包括一个非空的 01 01 01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过 30000 30000 30000。
输出格式:
在文本文件WIR.OUT的第一行输出一个单词:
TAK
——假如存在这样的代码;
NIE
——如果不存在。
输入输出样例
输入样例#1:
3
01
11
00000
输出样例#1:
NIE
解题分析
一开始ZZ了, 然后发现把 f a i l fail fail边加进来就是 A C AC AC自动机上找环…
注意通过fail下传不能有的单词的标记。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <queue>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100500
int cnt, root, n;
int fail[MX], son[MX][2];
bool tag[MX], vis[MX], ins[MX];
char buf[MX];
std::queue <int> q;
void insert()
{
R int len = std::strlen(buf), now = root, id;
for (R int i = 0; i < len; ++i)
{
id = buf[i] - '0';
if (!son[now][id]) son[now][id] = ++cnt;
now = son[now][id];
}
tag[now] = true;
}
void build()
{
R int now;
if (son[0][0]) q.push(son[0][0]);
if (son[0][1]) q.push(son[0][1]);
W (!q.empty())
{
now = q.front(); q.pop();
for (R int i = 0; i < 2; ++i)
{
if (son[now][i])
{
fail[son[now][i]] = son[fail[now]][i];
tag[son[now][i]] |= tag[fail[son[now][i]]];
q.push(son[now][i]);
}
else son[now][i] = son[fail[now]][i];
}
}
}
void DFS(R int now)
{
if (ins[now]) puts("TAK"), exit(0);
if (vis[now]) return;
vis[now] = ins[now] = true;
if (!tag[son[now][0]]) DFS(son[now][0]);
if (!tag[son[now][1]]) DFS(son[now][1]);
ins[now] = false;
}
int main(void)
{
scanf("%d", &n);
W (n--) scanf("%s", buf), insert();
build();
DFS(0);
puts("NIE");
}