简化题意:
二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例:
例如如果 \(\{011, 11, 00000\}\) 为病毒代码段,那么一个可能的无限长安全代码就是 \(010101…\) 。如果 \(\{01, 11, 000000\}\) 为病毒代码段,那么就不存在一个无限长的安全代码。
任务:
请写一个程序:
- 读入病毒代码;
- 判断是否存在一个无限长的安全代码;
- 将结果输出。
如果直接构造该串的话,时间复杂度显然会爆炸。
考虑AC自动机上该串匹配的性质。
很明显在AC自动机上,这个无限长的字符串永远不会到达一个标记节点,只会在AC自动机上的一个环上不断匹配。
所以我们只要找到这个环就行了。
注意如果一个节点的 \(fail\) 节点如果是标记节点,那么它自身也是标记节点,因为该节点的 \(fail\) 节点到根节点的串是该节点到根节点的串的后缀。
#include<bits/stdc++.h>
const int maxn=3e4+10;
namespace IO
{
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
}
using IO::read;
struct Aho_Corasick_AutoMaton
{
int son[maxn][2],fail[maxn],cnt;
bool v[maxn];
inline void add(char *s)
{
int len=strlen(s+1), now=0;
for (int i=1; i<=len; ++i)
{
int c=s[i]-'0';
if (!son[now][c]) son[now][c]=++cnt;
now=son[now][c];
}
v[now]=true;
}
inline void build()
{
std::queue<int>q;
for (int i=0; i<2; ++i)
if (son[0][i]) q.push(son[0][i]);
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=0; i<2; ++i)
if (son[x][i]) fail[son[x][i]]=son[fail[x]][i], v[son[x][i]]|=v[fail[son[x][i]]], q.push(son[x][i]);
else son[x][i]=son[fail[x]][i];
}
}
} ACAM;
bool vis[maxn],ins[maxn];
inline void dfs(int x)
{
if (ins[x]) { puts("TAK"); exit(0); }
if (vis[x] || ACAM.v[x]) return ;
vis[x]=ins[x]=1;
if (ACAM.son[x][0]) dfs(ACAM.son[x][0]);
if (ACAM.son[x][1]) dfs(ACAM.son[x][1]);
ins[x]=0;
}
char ch[maxn];
int main()
{
int n;read(n);
for (int i=1; i<=n; ++i) scanf("%s",ch+1), ACAM.add(ch);
ACAM.build();
dfs(0);
puts("NIE");
return 0;
}
原文:https://www.cnblogs.com/G-hsm/p/11429544.html