洛谷P2444:病毒——AC自动机
P2444 [POI 2000] 病毒
题目描述
二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例:
例如如果 \(\{011, 11, 00000\}\) 为病毒代码段,那么一个可能的无限长安全代码就是 \(010101 \ldots\)。如果 \(\{01, 11, 000000\}\) 为病毒代码段,那么就不存在一个无限长的安全代码。
现在给出所有的病毒代码段,判断是否存在无限长的安全代码。
输入格式
第一行包括一个整数 \(n\),表示病毒代码段的数目。
以下的 \(n\) 行每一行都包括一个非空的 \(01\) 字符串,代表一个病毒代码段。
输出格式
如果存在无限长的安全代码,输出 TAK,否则输出 NIE。
输入输出样例 #1
输入 #1
3
01
11
00000
输出 #1
NIE
说明/提示
\(1 \leq n \leq 2000\),所有病毒代码段的总长度不超过 \(3 \times 10^4\)。
思路
其实不算特别难(凡是我能一下子看懂题解的题都不算难),运用逆向思维,我们考虑一个满足题意的字符串s拿到Trie树上匹配会怎样?
\(A:\)一直不会碰到危险节点
赤裸裸地指向了一个东西——环 .
且该环要满足:1.不经过危险节点 2.从根节点到环要至少存在一条不经过危险节点的路径
所以我们从根节点开始dfs,遇到危险节点就避开,看能否走回到一个之前标记的可走的点,即形成一个环.代码真的很好理解QwQ
注意:一定要记忆化!!!不然...... rt:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define lid (id<<1)
#define rid (id<<1|1)
const int N=1e5+10;
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}return x*f;}inline void write(int x){if(x<0)x*=-1,putchar('-');if(x>9)write(x/10);putchar(x%10+'0');return;}inline int max(int x,int y){return (x<y)?y:x;}inline int min(int x,int y){return (x<y)?x:y;}
int n,tot;
int tr[N][2],fail[N];
int vis[N];
char s[N];
inline void build(char* t)
{
int l=strlen(t),rt=0;
for(int i=0;i<l;i++)
{
int p=t[i]-'0';
if(!tr[rt][p]) tr[rt][p]=++tot;
rt=tr[rt][p];
}
vis[rt]=1;
}
inline void getfail()
{
queue<int> q;
for(int i=0;i<2;i++) if(tr[0][i]) q.push(tr[0][i]);
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=0;i<2;i++)
{
if(tr[u][i]) fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]);
else tr[u][i]=tr[fail[u]][i];
if(vis[fail[tr[u][i]]]) vis[tr[u][i]]=1;//如果失配指针指向危险节点,则该节点也为危险节点
}
}
}
int ins[N],used[N];
bool dfs(int x)
{
ins[x]=1;
for(int i=0;i<2;i++)
{
int v=tr[x][i];
if(ins[v])return 1;//走回到某个可走的点,形成环
if(vis[v]) continue;//危险节点,此路不通
if(used[v])continue;//记忆化,优化时间复杂度
used[v]=1;
if(dfs(v))return 1;//接着往下找
}
ins[x]=0;
return 0;
}
signed main()
{
n=read();
for(int i=1;i<=n;i++)
{
cin>>s;
build(s);
}
getfail();
if(dfs(0)) cout<<"TAK";
else cout<<"NIE";
return 0;
}
欢迎指出问题或错误,点个推荐再走啦(卖萌)

浙公网安备 33010602011771号