[POI2000]病毒
题目描述
二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例:
例如如果 { 011 , 11 , 00000 } \{011, 11, 00000\} {011,11,00000} 为病毒代码段,那么一个可能的无限长安全代码就是 010101 … 010101 \ldots 010101…。如果 { 01 , 11 , 000000 } \{01, 11, 000000\} {01,11,000000} 为病毒代码段,那么就不存在一个无限长的安全代码。
现在给出所有的病毒代码段,判断是否存在无限长的安全代码。
输入格式
第一行包括一个整数 n n n,表示病毒代码段的数目。
以下的 n n n 行每一行都包括一个非空的 01 01 01 字符串,代表一个病毒代码段。
输出格式
如果存在无限长的安全代码,输出 TAK
,否则输出 NIE
。
样例 #1
样例输入 #1
3
01
11
00000
样例输出 #1
NIE
提示
1 ≤ n ≤ 2000 1 \leq n \leq 2000 1≤n≤2000,所有病毒代码段的总长度不超过 3 × 1 0 4 3 \times 10^4 3×104。
1、 危险点, cnt[k] != 0的点。
因为fail指向的是当前串的最长后缀,
fail指向的后缀都是病毒了,那当前串本身一定也是病毒了。
因此,建立 fail 树, 从根节点出发,如果当前节点k是危险点( cnt[k] != 0)
那么其孩子节点也要置为危险点。
2、 找环
对于这个题只需要建好trie图,然后在trie图上找一个环
为什么呢?因为我们如果拿一个安全代码在自动机上跑
一定会不停的跑而到不了单词的结束点,这就要求必须有个环
而且这个环要求必须经过根节点,且不经过一些危险点。
开连个全局数组,
vis[k] 表示节点k是否被访问过
instk[k] 表示节点是否在栈中
当访问到某个节点x 在栈中,说明找到了环。
注意每次进入 dfs(x), 离开x点时候,把 instk[x] = 0 置零
#include <bits/stdc++.h>
using namespace std;
const int N = 3e4 + 10;
int n;
string s[N];
int ch[N][2], cnt[N], idx;
int ne[N];
bool vis[N], instk[N]; //instk 表示是否在栈中
bool flag;
int tot;
int ver[N], head[N], Next[N];
void add(int x, int y)
{
ver[++tot] = y, Next[tot] = head[x], head[x] = tot;
}
void insert(int k)
{
int p = 0, len = s[k].size();
for(int i = 0; i < len; ++i)
{
int j = s[k][i] - '0';
if(!ch[p][j])
ch[p][j] = ++idx;
p = ch[p][j];
}
cnt[p]++;
}
void dfs1(int x)
{
for(int i = head[x]; i; i = Next[i])
{
int y = ver[i];
if(cnt[x])
cnt[y] = cnt[x];
dfs1(y);
}
}
void build()
{
queue<int> q;
for(int i = 0; i < 2; ++i)
{
if(ch[0][i])
q.push(ch[0][i]);
}
while(q.size())
{
int u = q.front(); q.pop();
for(int i = 0; i < 2; ++i)
{
int v = ch[u][i];
if(v)
{
ne[v] = ch[ne[u]][i], q.push(v);
}else{
ch[u][i] = ch[ne[u]][i];
}
}
}
for(int v = 1; v <= idx; ++v)
{
add(ne[v], v);
}
dfs1(0);
}
void dfs(int x)
{
if(instk[x])
{
flag = true;
return;
}
if(cnt[x] || vis[x])
return;
vis[x] = true, instk[x] = true;
dfs(ch[x][0]);
dfs(ch[x][1]);
instk[x] = false;
}
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; ++i)
cin >> s[i], insert(i);
build();
dfs(0);
if(flag)
{
printf("TAK\n");
}else{
printf("NIE\n");
}
return 0;
}