洛谷P2444 [POI2000]病毒

Description

二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例:
例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。
任务:
请写一个程序:
l 读入病毒代码;
l 判断是否存在一个无限长的安全代码;
l 将结果输出


Input

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


Output

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

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

l NIE——如果不存在。


Solution

考场上一眼AC自动机
然后没搞出来。
其实就水得一匹。
但我字符串也是水得一匹。

以nxt数组为图。
很容易有从根开始的链必然不可以。
也易知如果有一个不包括root的环就可以一直跑跑跑,即是无限长的安全代码。
那么直接大力搜索判环就完了。


Code

#include<bits/stdc++.h>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
using namespace std;
int nxt[200010][2];
int fail[200010],n;
int head[200010],cnt;
bool vir[200010];
bool vis[200010];
bool ins[200010];
bool flag=0;
struct data{
    int u,s;
};
struct trie{
    int root,len;
    int newnode(){
        memset(nxt[len],0,sizeof(nxt[len]));
        return (++len)-1;
    }
    void init(){
        len = 0;
        root = newnode();
    }
    void insert(char s[],int num){
        int len = strlen(s);
        int u = root;
        for(int i = 0;i < len;i++){
            int r=s[i]-'0';
            if(!nxt[u][r])
            nxt[u][r] = newnode();
            u = nxt[u][r];
        }
        vir[u]=1;
    }
    void get_fail(){
        queue<int>q;
        fail[root] = root;
        for(int i = 0;i < 2;i++){
            if(!nxt[root][i])
            nxt[root][i] = root;
            else{
                fail[nxt[root][i]] = root;
                q.push(nxt[root][i]);
            }
        }
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = 0;i < 2;i++){
                if(!nxt[u][i])
                nxt[u][i] = nxt[fail[u]][i];
                else{
                    int v=fail[u];
                    while(v&&!nxt[v][i]) v=fail[v];
                    fail[nxt[u][i]]=nxt[v][i];
                    vir[nxt[u][i]]|=vir[nxt[v][i]];
                    q.push(nxt[u][i]);
                }
            }
        }
    }
    bool dfs(int x){
       	ins[x]=1;
     	for(int i=0;i<2;i++){
            int v=nxt[x][i];
            if(ins[v]) return 1;
       	    if(vis[v]||vir[v]) continue;
       	    vis[v]=1;
       	    if(dfs(v)) return 1;
       	}
    	ins[x]=0;
    	return 0;
    }
}tree;
char s[200010];
int main(){
    tree.init();
    scanf("%d",&n);
    for(int i = 0;i < n;i++){
    	scanf("%s",s);
        tree.insert(s,i);
    }
    tree.get_fail(); 
	flag=tree.dfs(0);
	printf("%s",flag?"TAK":"NIE");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值