[POI2000]病毒

[POI2000] 病毒


1.题目

题目描述

二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。

示例:例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。

任务:请写一个程序:

1.在文本文件WIR.IN中读入病毒代码;

2.判断是否存在一个无限长的安全代码;

3.将结果输出到文件WIR.OUT中。

输入输出格式

输入格式:

在文本文件WIR.IN的第一行包括一个整数\(n(n \leqslant 2000)\) ,表示病毒代码段的数目。以下的\(n\)行每一行都包括一个非空的01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过30000。

输出格式:

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

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

NIE——如果不存在。

输入输出样例

输入样例#1:

3
01 
11 
00000

输出样例#1:

NIE

2.题解

此题考虑将病毒串构建成一个AC自动机;病毒代码即会与某个串匹配,而安全代码则不会。所以构建好AC自动机后沿\(fail\)指针跳转,直到找到一个环即可。找环的操作可以使用dfs完成。

代码:

#include <algorithm>
#include <cstdio>
const int MAXN = 2010;
const int MAXLEN = 30010;
struct Node{
    int fail;
    int next[2];
    bool exist;
}tree[MAXLEN];
struct QUEUE{
    int head, tail;
    int a[MAXLEN];
    QUEUE() {head = tail = 0;}
    bool empty() {return (head == tail);}
    void push(int x) {a[++tail] = x;}
    int pop() {return (a[++head]);}
    void clean() {head = tail = 0;}
}que;
int n, top;
char str[MAXLEN];
bool vis[MAXLEN], nowvis[MAXLEN];
inline void insert() {
    int iter(0), tmp(0), now(0);
    for (; str[iter]; ++iter) {
        tmp = str[iter] - '0';
        if(!tree[now].next[tmp]) tree[now].next[tmp] = ++top;
        now = tree[now].next[tmp];
    }
    tree[now].exist = true;
    return ;
}
inline void Make_fail() {
    int now(0), tmp(0);
    if(tree[0].next[0]) tree[tree[0].next[0]].fail = 0, que.push(tree[0].next[0]);
    if(tree[0].next[1]) tree[tree[0].next[1]].fail = 0, que.push(tree[0].next[1]);
    while(!que.empty()) {
        now = que.pop();
        if((tmp = tree[now].next[0])) {
            tree[tmp].fail = tree[tree[now].fail].next[0];
            que.push(tmp);
            if(tree[tree[tmp].fail].exist)
                tree[tmp].exist = true;
        }
        else tree[now].next[0] = tree[tree[now].fail].next[0];
        if((tmp = tree[now].next[1])) {
            tree[tmp].fail = tree[tree[now].fail].next[1];
            que.push(tmp);
            if(tree[tree[tmp].fail].exist)
                tree[tmp].exist = true;
        }
        else tree[now].next[1] = tree[tree[now].fail].next[1];
    }
}
inline void dfs(int now) {
    nowvis[now] = true; int tmp(0);
    for (int i = 0; i <= 1; ++i) {
        if(nowvis[(tmp = tree[now].next[i])]) {
            printf("TAK\n"); std::exit(0);
        }
        else if(!tree[tmp].exist && !vis[tmp]) {
/*在这里,因为我们在求Fail指针的时候将此节点的空儿子修改为其Fail节点的儿子,即失配的时候一步即可转移到位,所以这里我们两个子节点都要dfs。这既保证了有Fail指针一定跳跃,也保证了它的实际子节点也可以搜索到。*/
            vis[tmp] = true;
            dfs(tmp);
        }
    }
    nowvis[now] = false;
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%s", str);
        insert();
    }
    Make_fail();
    dfs(0);
    printf("NIE\n");
    return 0;
}

附题目传送门:传送门

猜你喜欢

转载自www.cnblogs.com/manziqi/p/9264887.html