zoukankan      html  css  js  c++  java
  • [BZOJ2938]:[Poi2000]病毒(AC自动机)

    题目传送门


    题目描述

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

    输入格式

    第一行包括一个整数n,表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。

    输出格式

    第一行输出一个单词。假如存在这样的代码,则输出TAK,否则输出NIE

    样例

    样例输入:
    3
    01
    11
    00000
    样例输出:
    NIE

    数据范围与提示

    对于全部数据,所有病毒代码段的总长度不超过3×104

    题解

    一看是多模式串,首先应该想到是AC自动机。

    如果还不会AC自动机,可以转到这篇博客,个人感觉还是写的挺清楚的:AC自动机讲解+[HDU2222]:Keywords Search(AC自动机)

    那么我们考虑怎么去处理。

    显然如果存在这么一个无限长的安全串的话,说明它在AC自动机里一直匹配不上,也就是说我们需要找到一个环,这个环中的每一个点都没有任何串在这个点结束。

    首先,将所有的文字段压入AC自动机,然后搞Fail指针,基本操作,不再赘述。

    end数组的含义为:第i个点有没有串在这里结束,有则为1,没有则为0

    不过需要注意的是,如果一个点的Fail指针指向的点的end1,那么它也要为1,因为如果它的Fail指针指向的点的end1的话,说明从根结点到Fail所组成的串是从跟节点到所组成的串的后缀。
    这些都处理好之后,我们就看着是不是一个环就好了。

    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    int trie[30001][2],cnt=1;
    char s[30001];
    bool end[30001],flag[30001],vis[30001];
    int nxt[30001],que[30001];
    void insert(char *str)//构建Trie树
    {
    	int p=1;
    	int len=strlen(str);
    	for(int i=0;i<len;i++)
    	{
    		int ch=str[i]-'0';
    		if(!trie[p][ch])trie[p][ch]=++cnt;
    		p=trie[p][ch];
    	}
    	end[p]=1;//结尾点标记为1
    }
    void build()
    {
    	for(int i=0;i<2;i++)
    		trie[0][i]=1;
    	que[1]=1;
    	for(int head=1,tail=1;head<=tail;head++)
    		for(int i=0;i<2;i++)
    			if(!trie[que[head]][i])trie[que[head]][i]=trie[nxt[que[head]]][i];
    			else
    			{
    				que[++tail]=trie[que[head]][i];
    				nxt[trie[que[head]][i]]=trie[nxt[que[head]]][i];
    				end[trie[que[head]][i]]|=end[nxt[trie[que[head]][i]]];//如果end[nxt[x]]的话,说明root->x是root->fail后缀。
    			}
    }
    bool find(int x)
    {
    	vis[x]=1;
    	for(int i=0;i<2;i++)
    	{
    		if(vis[trie[x][i]])return 1;//如果这个点被访问过了,那么说明出现了一个环。
    		if(end[trie[x][i]]||flag[trie[x][i]])continue;//发现这条路走不下去(end为1),或者是已经被走过了(flag为1)。
    		flag[trie[x][i]]=1;//标记已经走过了这个点。
    		if(find(trie[x][i]))return 1;//接着走下一个点。
    	}
    	vis[x]=0;
    	return 0;
    }
    int main()
    {
    	int n;
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%s",s);
    		insert(s);
    	}
    	build();
    	if(find(0))printf("TAK");
    	else printf("NIE");
    	return 0;
    }
    

    rp++

  • 相关阅读:
    MongoDB安装
    前端构建工具gulp入门教程
    限制input输入类型(多种方法实现)
    【E20200105-1】Centos 7.x 下vsftpd配置文件常用配置详解
    【E20200102-1】centos 7 下vsftp的安装和配置
    【E20200101-1】Centos 7.x 关闭防火墙(firewall)和SELinux
    Linux下如何修改用户默认目录
    linux给普通用户增加ssh权限
    在IIS7中应用Application Request Routing配置反向代理
    vmware相关服务默认禁用 修改服务弹出服务拒绝访问解决办法
  • 原文地址:https://www.cnblogs.com/wzc521/p/11081909.html
Copyright © 2011-2022 走看看