[bzoj2938][Poi2000]病毒——AC自动机

探讨使用AC自动机构建无限01序列的可行性,通过DFS寻找Trie树中的循环路径,确保序列不会完全匹配任何给定的有限子序列。

题目大意:

给定n个01串,求是否存在一个长度无穷的01串使得这个01串不包含任何一个给定的串。

思路:

考虑AC自动机匹配的过程是在Trie树上不停地跳,那么如果我们可以找到一个串使得这个串可以一直在Trie上跳并且永远跳不到匹配节点就说明可行。
可以发现这样的话这个串在AC自动机上的匹配一定是会出现环的,于是我们直接dfs找环就好了。
考虑如何判断这个节点是否可以走到,即这个节点以及这个节点的所有fail都没有匹配才可以走,在求fail的时候可以预处理出来。
dfs找环的时候记得要开两个bool数组,一个标记是否来过,一个标记是否在栈中。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj2398.in","r",stdin);
    freopen("bzoj2398.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}

const int maxn=6e4+10;
int n;
int ans[maxn],tp;

struct AC_Automaton{
    int ch[maxn][2],num[maxn],fail[maxn],cnt;
    bool vis[maxn],in[maxn];
    void insert(char *s){
        int len=strlen(s+1),u=0,c;
        REP(i,1,len){
            c=s[i]-'0';
            if(!ch[u][c])ch[u][c]=++cnt;
            u=ch[u][c];
        }
        ++num[u];
    }
    void build(){
        int h=1,t=0,q[maxn];
        REP(i,0,1)if(ch[0][i])
            q[++t]=ch[0][i];
        while(h<=t){
            int u=q[h++];
            REP(i,0,1){
                int v=ch[u][i];
                if(v)q[++t]=v;
                if(v){
                    fail[v]=ch[fail[u]][i];
                    num[v]|=num[fail[v]];
                }
                else ch[u][i]=ch[fail[u]][i];
            }
        }
    }
    bool dfs(int u){
        if(num[u])return false;

        if(in[u])return true;
        if(vis[u])return false;

        vis[u]=in[u]=1;
        if(dfs(ch[u][0]) || dfs(ch[u][1]))return true;

        in[u]=0;
        return false;
    }
}T;

int main(){
    File();
    char s[maxn];
    read(n);
    REP(i,1,n){
        scanf("%s",s+1);
        T.insert(s);
    }

    T.build();

    printf("%s\n",T.dfs(0) ? "TAK" : "NIE");

    REP(i,1,tp)printf("%d",ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/ylsoi/p/10181266.html

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值