zoukankan      html  css  js  c++  java
  • [洛谷P2444] [POI2000]病毒

    洛谷题目链接:[POI2000]病毒

    题目描述

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

    示例:

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

    任务:

    请写一个程序:

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

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

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

    输入输出格式

    输入格式:

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

    输出格式:

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

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

    NIE——如果不存在。

    输入输出样例

    输入样例#1:

    3
    01
    11
    00000

    输出样例#1:

    NIE

    题解: 显然我们是无法枚举一个无限长的串的,那么我们需要考虑找到这个构造方法.

    既然是多模式串匹配,这里我们考虑使用(AC)自动机.

    首先还是先建出(AC)自动机,然后我们考虑如何才能找到一个不被匹配的方法.我们知道如果匹配就是在(AC)自动机中访问到一个标记过的节点,或是该串的前缀中含有单词,所以我们可以在建树的过程中将有单词的点都打上标记,并且将它的所有子节点都打上标记,表示访问到这些节点就不存在合法答案.

    那么如何找合法答案呢?因为在(AC)自动机上的匹配过程需要通过(fail)指针来跳转,所以如果能找到一个包含(0)节点((trie)起始的空节点)的环,那么就可以一直在这个环上跳转一直不匹配成功,直接用(dfs)来找这个环就可以了.

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 3e4+5;
    
    int n, cntn, ch[2][N], fail[N], chk[N], vis[N];
    bool ok[N];
    char s[N];
    
    void insert(char *s){
        int len = strlen(s+1), now = 0;
        for(int i = 1; i <= len; i++){
            if(!ch[s[i]-'0'][now]) ch[s[i]-'0'][now] = ++cntn;
            now = ch[s[i]-'0'][now];
        }
        ok[now] = 0;
    }
    
    void build(){
        queue <int> q;
        if(ch[0][0]) q.push(ch[0][0]);
        if(ch[1][0]) q.push(ch[1][0]);
        while(!q.empty()){
            int x = q.front(); q.pop();
            for(int i = 0; i < 2; i++){
                if(ch[i][x]){
                    fail[ch[i][x]] = ch[i][fail[x]];
                    q.push(ch[i][x]);
                    ok[ch[i][x]] &= ok[ch[i][fail[x]]];
                }
                else ch[i][x] = ch[i][fail[x]];
            }
        }
    }
    
    void dfs(int x){
        if(vis[x]) cout << "TAK" << endl, exit(0);
        if(chk[x] || !ok[x]) return;
        vis[x] = chk[x] = 1;
        dfs(ch[0][x]), dfs(ch[1][x]);
        vis[x] = 0;
    }
    
    int main(){
        cin >> n;
        memset(ok, 1, sizeof(ok));
        for(int i = 1; i <= n; i++) cin >> s+1, insert(s);
        build();
        dfs(0);
        cout << "NIE" << endl;
        return 0;
    }
    
  • 相关阅读:
    Weekly blog for week 1908
    Weekly blog for week 1907
    Weekly blog for week 1905&1906
    Weekly blog for week 1904
    Write Week 3 Blog beforehand
    Add Week 2 Blog
    First week of a new year
    Andriod-Log
    bat
    学习方法
  • 原文地址:https://www.cnblogs.com/BCOI/p/10460224.html
Copyright © 2011-2022 走看看