[POI2000] 病毒

题目链接(这次是落咕的):戳我
做这个题目之前可以考虑先去把落咕上的两个AC自动机的模板写了——这个 \(\;\)还有这个

AC自动机是什么呢?是一种多模匹配的算法。有可能很多人都说它是KMP+Trie。

一般的AC自动机解决的是字符串匹配一类的问题,但是这道题绕了个弯,让求有没有可能存在一个无限长的串,使得其中不包含给定的一些文本串——也就是说存不存在一个串,无法匹配给定的这些串。

那么也就是说,要尽可能地让模式串失配。有fail指针就要往上面跳。我们在预先处理fail指针的时候就把最后节点的儿子指向了自己fail指针所指的节点的相同的儿子。之后在建出来的trie树上跑dfs,如果处理当前匹配可以跳到一个环里,那么自然可以构成一个无限长的字符串满足题意。反之,则每次往下一层必定有匹配,最终一定会匹配上病毒代码。

所以就是AC自动机+dfs!需要注意的一点是,我们要给病毒串的末尾打标记,然后它的子节点必须也要继承上这个标记(因为如果匹配到这个子节点,那么一定匹配了它的父亲,匹配到它的父亲的时候就已经是匹配上病毒串了)。dfs的时候如果遇到标记直接return,因为这样就匹配上了病毒串,而我们要找的是无法完成匹配的串。还有一点,就是如果遇到非当前次经过的节点(对应代码为pre[now]==1),也要直接return。因为如果遇到了它,那么意味着这之前这个节点并没有成功跳进环,所以进入到这个节点是没用的。

最后注意既然我们是要找当前无法完成匹配(也就是跳进环)的一些节点,那么我们处理完一定要把回溯。。。。。

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#define MAXN 100010
using namespace std;
struct tree{int fail,end,vis[26];}ac[MAXN*10];
int cnt=0,n,t;
int head[MAXN],done[MAXN],pre[MAXN];
string s[2010];
struct Edge{int nxt,to;}edge[MAXN<<1];
inline void add(int from,int to){edge[++t].nxt=head[from],edge[t].to=to,head[from]=t;}
inline void build(string s)
{
    int len=s.length();
    int now=0;
    for(int i=0;i<len;i++)
    {
        if(ac[now].vis[s[i]-'0']==0)
        {
            ac[now].vis[s[i]-'0']=++cnt;
            add(now,ac[now].vis[s[i]-'0']);
        }
        now=ac[now].vis[s[i]-'0'];
    }
    ac[now].end=1;
}
inline void get_fail()
{
    queue<int>q;
    for(int i=0;i<=1;i++)
        if(ac[0].vis[i])
            ac[ac[0].vis[i]].fail=0,q.push(ac[0].vis[i]);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=0;i<=1;i++)
        {
            if(ac[u].vis[i]!=0)
            {
                ac[ac[u].vis[i]].fail=ac[ac[u].fail].vis[i];
                q.push(ac[u].vis[i]);
                if(ac[ac[u].fail].end==1) ac[u].end=1;
            }
            else ac[u].vis[i]=ac[ac[u].fail].vis[i];
        }
    }
}
inline void dfs(int now)
{
    if(ac[now].end) return;
    if(done[now]){printf("TAK\n");exit(0);}
    if(pre[now]) return;
    pre[now]=done[now]=1;
    for(int i=0;i<=1;i++)
        if(ac[now].vis[i])
            dfs(ac[now].vis[i]);
    done[now]=0;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&n);
    for(int i=1;i<=n;i++) cin>>s[i],build(s[i]);
    get_fail();
    dfs(0);
    printf("NIE\n");
    return 0;
}
posted @ 2019-01-21 23:05  风浔凌  阅读(302)  评论(0编辑  收藏  举报