• 「题解」:[BZOJ2938]病毒 (AC自动机+dfs)


    题目描述


     二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。

    示例:
    例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。
    任务:
    请写一个程序:
    l 读入病毒代码;
    l 判断是否存在一个无限长的安全代码;
    l 将结果输出

     输入

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

    输出

    你应在在第一行输出一个单词:

    l TAK——假如存在这样的代码。
    l NIE——如果不存在。

    样例输入

    3
    01
    11
    00000

    样例输出

    NIE
     
    题解

    多模式串匹配问题。
    果断AC自动机(因为蒟蒻我也不会别的啊QAQ
    把每个病毒串插进trie树,get_fail这些基本操作大家想必都会啦
    本题多了一个奇诡操作:fail环。
    首先了解一下fail环
    拉过来一张丑陋的图:
    如上图为题目描述中为NIE的那个数据(好像少了个0……)。
    显然上图中虚线表示的0并不存在。
    所以dfs搜到他的时候,这个点指向root的右儿子的那个1的左儿子。
    不过遗憾的是
    这个0也不存在。
    那么只好走这个1的fail指针,即root的右儿子,
    然后我们发现,它还是会从root的右儿子去找到当前这个虚线框里的0
    这次这个0指向了root的左儿子。
    然后再顺下来我们发现又可以找到原来的1啦。
    这就是fail环啦(至少我是这么理解的,大神别踩啊QAQ)
    然后捏?
    我们把插入时的endd设置成危险节点,
    即不能访问的节点
    然后一遍dfs就出来啦!
    代码:
    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<cmath>
    #define rint register int
    using namespace std;
    char ch[30004];
    int n;
    int trie[30004][2];
    int cnt=1,fail[30004];
    bool endd[30004],vis[30004];
    bool ans=false,failed[30004];
    inline void insert(char *str)
    {
    	int len=strlen(str),p=1;
    	for(rint i=0;i<len;++i)
    	{
    		int l=str[i]-'0';
    		if(!trie[p][l])
    			trie[p][l]=++cnt;
    		p=trie[p][l];
    	}
    	endd[p]=true;
    }
    inline void get_fail()
    {
    	queue <int>q;
    	q.push(1);
    	fail[1]=0;
    	trie[0][0]=trie[0][1]=1;
    	while(!q.empty())
    	{
    		int l=q.front();q.pop();
    		for(rint i=0;i<2;++i)
    		{
    			if(trie[l][i])
    			{
    				fail[trie[l][i]]=trie[fail[l]][i];
    				if(endd[fail[trie[l][i]]])
    					endd[trie[l][i]]=true;//注意这两句话,没加毁人生QAQ
    				q.push(trie[l][i]);
    			}
    			else trie[l][i]=trie[fail[l]][i];
    		}
    	}
    }
    inline void dfs(int u)
    {
    	vis[u]=1;
    	for(int i=0;i<=1;i++)
    	{
    		if(vis[trie[u][i]])
    		{
    			ans=true;
    			return ;
    		}
    		else if(!failed[trie[u][i]]&&!endd[trie[u][i]])
    		{
    			failed[trie[u][i]]=1;
    			dfs(trie[u][i]);
    		}
    	}
    	vis[u]=0;
    	return ;
    }
    int main()
    {
    //	freopen("wir213.in","r",stdin);
    	scanf("%d",&n);
    	for(rint i=1;i<=n;++i)
    	{
    		scanf("%s",ch);
    		insert(ch);
    	}
    	get_fail();
    //	for(rint i=1;i<=cnt;++i)cout<<fail[i]<<endl;
    	dfs(1);
    	if(ans)cout<<"TAK"<<endl;
    	else cout<<"NIE"<<endl;
    	return 0;
    }
    

     完结撒花~

  • 相关阅读:
    20220507 Using Spring Boot
    20220606 Java工具类,去重10亿手机号码
    20220605 JVM下篇:性能监控与调优篇 5. 分析 GC 日志
    20220506 Documentation Overview
    20220506 Upgrading Spring Boot Applications
    20220605 JVM下篇:性能监控与调优篇 4. JVM 运行时参数
    eclipse无法加载Layout(Eclipse is loading framework information and the layout library from the SDK fold)
    安卓app_sl3.8相对布局管理器
    安卓app_sl3.13xml按钮 onClick属性指定对应的方法与java事件监听器的方法
    安卓app_sl3.12登记注册信息获取编辑框的值打印到Log日志显示
  • 原文地址:https://www.cnblogs.com/xingmi-weiyouni/p/11082103.html
Copyright © 2020-2023  润新知