[POI2000]病毒(AC自动机)

题意

给定一些字符串,问是否存在一个无限长度的字符串,使得它不包含任何一个给定的串

思路

将所有串加入AC自动机,那么我们从根节点选择的一个字符串显然不能经过任何一个带有end标记的节点,不然就说明其中包含了这个节点对应的字符串。

于是用fail标记建成trie图,判断trie图上面有没有一个不经过end标记的环即可

Code:

#include<bits/stdc++.h>
#define N 30005
using namespace std;
int n;
int nxt[N][2],fail[N],ndsum;
char c[N];
bool is_end[N],vis[N],st[N];

void ad(char a[])
{
    int l=strlen(a),now=0;
    for(int i=0;i<l;++i)
    {
        int pp=a[i]-'0';
        if(!nxt[now][pp]) nxt[now][pp]=++ndsum;
        now=nxt[now][pp];
    }
    is_end[now]=1;
}
void getfail()
{		
    queue<int> q;
    for(int i=0;i<2;++i) if(nxt[0][i]) q.push(nxt[0][i]);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=0;i<2;++i)
        if(nxt[u][i]) fail[nxt[u][i]]=nxt[fail[u]][i],q.push(nxt[u][i]),is_end[nxt[u][i]]|=is_end[fail[nxt[u][i]]];
        else nxt[u][i]=nxt[fail[u]][i];
    }
}
bool dfs(int u)
{
    if(st[u]) return true;
    if(vis[u]) return false;
    vis[u]=st[u]=1;
    for(int i=0;i<2;++i)
    {
        if(nxt[u][i]&&!is_end[nxt[u][i]]) if(dfs(nxt[u][i])) return true;
    }
    st[u]=0;
    return false;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",c);
        ad(c);
    }
    getfail();
    memset(vis,0,sizeof(vis));
    if(dfs(0)) printf("TAK
");
    else printf("NIE
");
    return 0;
}
原文地址:https://www.cnblogs.com/Chtholly/p/11185755.html