bzoj2938

AC自动机+trie图优化

很明显就是要一个串不能匹配到任何一个病毒,那么我们就建一个AC自动机

不能匹配的话也就是一个节点不能是单词结束节点,fail指针也不能是结束节点

然后就卡壳了。。。zz

我们把自动机建成trie图,也就是不存在的节点直接指向原来fail指针,然后我们只要在这个图上找有没有环就可以了。

因为ac自动机当不能继续匹配,也就是没有儿子节点,就会沿着fail指针走,否则就沿着儿子走,于是我们就把不存在的儿子指向fail,是一种优化,如果有一个无限长的串,也就是这个串能不停地匹配,不会走到危险节点,那么肯定就有环了,因为自动机是有限的。

#include<bits/stdc++.h>
using namespace std;
const int N = 30010;
int n;
char s[N];
struct acautomation {
    int root, cnt;
    int child[N][2], fail[N], danger[N], vis[N], inq[N];
    void insert(char s[])
    {
        int now = root;
        for(int i = 0; i < strlen(s); ++i)
        {
            int x = s[i] - ‘0‘;
            if(!child[now][x]) child[now][x] = ++cnt;
            now = child[now][x];
        }
        danger[now] = 1;
    }
    void build_fail()
    {
        queue<int> q;
        for(int i = 0; i < 2; ++i) if(child[root][i])
        {
            q.push(child[root][i]);
            fail[child[root][i]] = 0;
        }
        while(!q.empty())
        {
            int u = q.front();
            q.pop();
            for(int i = 0; i < 2; ++i)
            {
                int v = child[u][i];
                if(!v) child[u][i] = child[fail[u]][i];
                else
                {
                    int now = fail[u];
                    while(now != root && !child[now][i]) now = fail[now];
                    fail[v] = child[now][i];
                    danger[v] |= danger[fail[v]];
                    q.push(v);
                }
            }
        }
    }
    bool dfs(int u)
    {
        inq[u] = 1;
        for(int i = 0; i < 2; ++i)
        {
            int v = child[u][i];
            if(inq[v]) return true;
            if(danger[v] || vis[v]) continue;
            vis[v] = 1;
            if(dfs(v)) return true;
        }
        inq[u] = 0;
        return false;
    }
} ac;
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
    {
        scanf("%s", s);
        ac.insert(s);
    }
    ac.build_fail();
    if(ac.dfs(0)) puts("TAK");
    else puts("NIE");
    return 0;
}

时间: 2024-10-14 04:15:57

bzoj2938的相关文章

【bzoj2938】【poi2000】病毒

题解: 对病毒串建立ac自动机: 有一个无限长的串等价于可以一直在自动机上匹配,等价于自动机上的转移有环: 当然前提是去掉病毒节点的fail子树: 写一个dfs记录是否在栈中,来过没有找到就不必再来了再记录一个vis保证复杂度; 然而......我在找环的时候呆了很久,最后写了tarjan: 如果你也是有些tarjan的危险想法的话注意特判转移的自环的情况: 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=30010

BZOJ2938: [Poi2000]病毒

传送门 AC自动机上的各种处理一直让我很迷.一个主要懵逼的点是不清楚对于不存在的字母所对应的方案如何处理.做了两道题大概比较清楚了. 其实对于不存在的字母节点不需要特殊的处理.因为如果累加方案的话,其方案会自动累加到$root$节点.而其fail指针也会自动指向自己. 另外这道题的判环参考了黄学长的代码,类似于一个简化版的tarjan. //BZOJ 2938 //by Cydiater //2016.10.18 #include <iostream> #include <cstdio&

BZOJ2938 [Poi2000]病毒 【AC自动机】

题目 二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码.如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的.现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码. 示例: 例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101-.如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码. 任务: 请写一个程序: l 读入病毒代码: l 判断是否存在一个无限长的

bzoj2938 病毒

在Trie图上找环,若有环则说明可以组成一个无限长的串 1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 int n,tot; 7 char s[300005]; 8 bool ed[300005]; 9 bool usd[300005]; 10 bool vis[300005]; 11 struct

2015湖南省队集训DAY6——B题(BZOJ4179)

题面挺扯的,我就直接说人话算了. 题目大意:给你若干个病毒串,问你能不能构造出长度大于n的字符串使其中不出现任何一个字符串. 多组数据,总文件大小小于1M 题解: 联动:BZOJ2938 基本是原题,稍作了改动. 考虑ac自动机. 所求即为ac自动机中是否存在长度大于等于l的路径 先将所有的串插进去,然后构造失配指针. 显然的,插入后的末端节点肯定是不能经过的. 但仅这样显然是不可以的,我们考虑在匹配时,如果失配指针指向的节点是danger节点,那么这个节点也是不能经过的(显然). 所以考虑ac

[转载]hzwer的bzoj题单

counter: 664BZOJ1601 BZOJ1003 BZOJ1002 BZOJ1192 BZOJ1303 BZOJ1270 BZOJ3039 BZOJ1191 BZOJ1059 BZOJ1202 BZOJ1051 BZOJ1001 BZOJ1588 BZOJ1208 BZOJ1491 BZOJ1084 BZOJ1295 BZOJ3109 BZOJ1085 BZOJ1041 BZOJ1087 BZOJ3038 BZOJ1821 BZOJ1076 BZOJ2321 BZOJ1934 BZOJ