【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;
}
原文地址:https://www.cnblogs.com/GXZlegend/p/6265366.html