P2444 [POI2000]病毒

传送门

AC自动机

如果有一个无限长的安全串

那么这个串在所有危险代码构成的AC自动机上一直匹配下去都不会走到结束标记

因为如果走到结束标记说明串中有危险代码

考虑怎样才能无限匹配

可以发现,如果在AC自动机上从根节点出发,一直走能走出一个环的话

那么就可以一直走这个环,从而无限匹配下去

所以要在自动机上找环

可以用DFS实现

有一点要注意

不但有结尾标记的点不能走

如果一个点一直走 fail 能走到一个有结束标记的点

那么这个点也不能走

因为这串的后面一部分会包含危险代码(AC自动机的性质)

然后就是代码了

 

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int N=3e4+7;
int n;
int c[N][2],pd[N],fail[N],cnt;
char a[N];
inline void ins()//插入危险代码
{
    int u=0,l=strlen(a);
    for(int i=0;i<l;i++)
    {
        int v=a[i]-'0';
        if(!c[u][v]) c[u][v]=++cnt;
        u=c[u][v];
    }
    pd[u]=1;
}
queue <int> q;
inline void pre()//预处理fail以及不能走的点
{
    for(int i=0;i<=1;i++) if(c[0][i]) q.push(c[0][i]);
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=0;i<=1;i++)
        {
            int v=c[u][i];
            if(!v) c[u][i]=c[fail[u]][i];
            else
            {
                fail[v]=c[fail[u]][i];
                if(pd[fail[v]]) pd[v]=1;
                q.push(v);
            }
        }
    }
}
bool vis[N],p[N];//vis表示是否走过,p表示是否在当前路径上
inline void dfs(int x)//dfs找环
{
    if(p[x])//如果在当前路径上,说明找到环了
    {
        cout<<"TAK";
        exit(0);
    }
    if(vis[x]||pd[x]) return;
    vis[x]=p[x]=1;
    dfs(c[x][0]); dfs(c[x][1]);
    p[x]=0;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",a);
        ins();
    }
    pre();
    dfs(0);
    cout<<"NIE";
    return 0;
}

 

posted @ 2018-09-20 18:06  LLTYYC  阅读(199)  评论(0编辑  收藏  举报