• 【bzoj2938】[Poi2000]病毒 AC自动机


    题目描述

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

    输入

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

    输出

    你应在在文本文件WIN.OUT的第一行输出一个单词:
    l TAK——假如存在这样的代码;
    l NIE——如果不存在。

    样例输入

    3
    01
    11
    00000

    样例输出

    NIE


    题解

    AC自动机

    如果一个串能无限长,那么说明它可以一直匹配,而始终不匹配病毒串。

    每次查找到下一个字符时,只有两种可能:

          (1)存在子节点,进入该子节点。

          (2)不存在子节点,进入fail节点,直至存在子节点。

    那么是否能无限匹配,就转化为有没有相应的环。

    这里为了方便,将fail合并到子节点。

    除了排除病毒串节点以外,还应排除fail指向病毒串的节点,因为该串后缀为病毒串前缀。

    然后dfs判环即可。

    #include <cstdio>
    #include <cstring>
    #include <queue>
    using namespace std;
    queue<int> q;
    int nt[30001][2] , fail[30001] , cnt[30001] , tot = 1;
    char str[30001];
    bool vis[30001] , ins[30001] , flag;
    int deep[30001] , low[30001] , sta[30001] , ind , h;
    void build()
    {
        int u , t , i;
        q.push(1);
        nt[0][0] = nt[0][1] = 1;
        while(!q.empty())
        {
            u = q.front();
            q.pop();
            for(i = 0 ; i < 2 ; i ++ )
            {
                if(nt[u][i])
                {
                    q.push(nt[u][i]);
                    t = fail[u];
                    while(t && !nt[t][i])
                        t = fail[t];
                    fail[nt[u][i]] = nt[t][i];
                    cnt[nt[u][i]] |= cnt[nt[t][i]];
                }
                else
                    nt[u][i] = nt[fail[u]][i];
            }
        }
    }
    bool dfs(int x)
    {
        ins[x] = vis[x] = 1;
        int i , y;
        for(i = 0 ; i < 2 ; i ++ )
        {
            y = nt[x][i];
            if(ins[y] || (!vis[y] && !cnt[y] && dfs(y)))
                return 1;
        }
        ins[x] = 0;
        return 0;
    }
    int main()
    {
        int n , i , t , l;
        scanf("%d" , &n);
        while(n -- )
        {
            scanf("%s" , str);
            l = strlen(str);
            t = 1;
            for(i = 0 ; i < l ; i ++ )
            {
                if(!nt[t][str[i] - '0'])
                    nt[t][str[i] - '0'] = ++tot;
                t = nt[t][str[i] - '0'];
            }
            cnt[t] = 1;
        }
        build();
        printf("%s
    " , dfs(1) ? "TAK" : "NIE");
        return 0;
    }
  • 相关阅读:
    0. 序列
    Megacli 常用
    4. Storm可靠性
    3. Storm编程框架
    2. Storm消息流
    1.1 Storm集群安装部署步骤
    poj3723,最 大 生成树
    次短路
    无间道之并查集
    最小生成树二Kruscal算法
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6265366.html
Copyright © 2020-2023  润新知