zoukankan      html  css  js  c++  java
  • 【BZOJ-2938】病毒 Trie图 + 拓扑排序

    2938: [Poi2000]病毒

    Time Limit: 1 Sec  Memory Limit: 128 MB
    Submit: 609  Solved: 318
    [Submit][Status][Discuss]

    Description

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

    Input

    第一行包括一个整数n,表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过30000。

    Output

    你应在在文本文件WIN.OUT的第一行输出一个单词:
    l         TAK——假如存在这样的代码;
    l         NIE——如果不存在。

    Sample Input

    3
    01
    11
    00000

    Sample Output

    NIE

    HINT

    Source

    Solution

    Trie图的一大经典应用。

    要构造一个无限长的安全串,显然是需要找至少一个安全的子串,然后循环下去,问题在于是否存在这样的子串。

    建出Trie图之后,满足条件的子串必须在Trie图上不断匹配,而且不断失配无法达到危险节点。

    这就说明,Trie图中存在不经过危险节点的环! 然后进行拓扑排序即可。

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    using namespace std;
    #define MAXN 30010
    int N;
    char s[MAXN];
    struct EdgeNode{int next,to;}edge[MAXN<<1];
    int head[MAXN],cnt=1,d[MAXN],visit[MAXN];
    inline void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
    inline void InsertEdge(int u,int v) {AddEdge(u,v); d[v]++; /*printf("%d ---> %d
    ",u,v);*/}
    namespace ACMachine
    {
        #define id(s) s-'0'
        int son[MAXN][2],end[MAXN],sz=1,fail[MAXN];
        inline void Insert(char str[])
        {
            int len=strlen(str+1),now=1;
            for (int i=1; i<=len; i++)
                if (son[now][id(str[i])]) now=son[now][id(str[i])];
                    else son[now][id(str[i])]=++sz,now=sz;
            end[now]=1;
        }
        inline void Getfail()
        {
            queue<int>q; q.push(1);
            while (!q.empty())
                {
                    int now=q.front(); q.pop(); 
                    end[now]|=end[fail[now]];
                    for (int i=0; i<=1; i++)
                            {
                                int fa=fail[now];
                                while (fa && !son[fa][i]) fa=fail[fa];
                                if (son[now][i]) 
                                    fail[son[now][i]]=fa? son[fa][i]:1,q.push(son[now][i]);
                                else son[now][i]=fa? son[fa][i]:1;
                            }
                }
        }
    }
    using namespace ACMachine;
    inline bool Topo()
    {
        queue<int>q;
        int sum=0;
        for (int i=1; i<=sz; i++)
            {
                if (end[i]) sum++; else
                for (int j=0; j<=1; j++)
                    if (!end[son[i][j]]) InsertEdge(i,son[i][j]);
            }
        for (int i=1; i<=sz; i++) if (!d[i] && !end[i]) q.push(i);
        while (!q.empty())
            {
                int now=q.front(); q.pop(); sum++;
                for (int i=head[now]; i; i=edge[i].next)
                    if (!--d[edge[i].to]) q.push(edge[i].to);
            }
        return sum==sz;
    }
    int main()
    {
        scanf("%d",&N);
        for (int i=1; i<=N; i++) scanf("%s",s+1),Insert(s);
        Getfail();
        if (Topo()) puts("NIE"); else puts("TAK");
        return 0;
    }
  • 相关阅读:
    C#中二进制,八进制,十六进制到十进制的相互转换
    Mac装Win10后没有无线网络的处理
    U盘容纳不了大于4G的文件比如ISO文件咋办?
    经典游戏“大富翁4”存档文件修改器Rich4Editor下载
    向C#的选项卡中添加自定义窗体
    C#对二进制文件的特定位置进行读写小结
    抗战剧中最耐看的《我的团长我的团》,最后结尾依然有神剧的影子
    绝大多数人努力程度之低,根本轮不上拼天赋
    ZT:与其怨天尤人,不如全力以赴;若想改变世界,你必须先从改变自己开始!
    java基础学习_多线程02_多线程、设计模式_day24总结
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/6139083.html
Copyright © 2011-2022 走看看