【BZOJ2938】病毒,AC自动机练习
2016-04-04 20:33
288 查看
传送门(权限题)
2938: [Poi2000]病毒
Time Limit: 1 Sec Memory Limit: 128 MB
Submit: 462 Solved: 240
[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
写在前面:给了三个样例的良心题
思路:
我们首先确定,如果可能,那么符合条件的字符串中一定有一个循环的字符串,如果不可能,那一定没有循环的字符串(因为循环的字符串是有循环节的,如果没有循环的字符串,那就意味着所有的循环节都不可以,那就是没有符合条件的了,反之亦如此)
然后偷个懒
zky:
首先我们把所有串建一个AC自动机
方便起见我们直接把fail指针合并到子结点
如果一个串能无限长,也就是说它可以在AC自动机上一直进行匹配但就是匹配不上
也就是说匹配指针不能走到val为1的结点,设这个点为x
即root..x是一个病毒串
那么fail指针指向x的y也不能走
因为root..x是root..y的一个后缀
处理出来判断有向图是否有环
dfs即可
建fail时把那些当前节点i没有的字符(相当于失配状态)都转移到fail[i]上去,之后我们只要从根上出发dfs一通乱走,如果途中遇到字符串结束的标记节点,就不走(因为一走不就相当于有这个病毒了吗),如果最后无路可走(怎么选都要经过标记节点)那就NiE了,但如果我们能走出一个没有标记节点的环,那就说明我们可以找到一个循环节使字符串合法且无限延长下去了
注意:
DFS时注意及时退出,减少搜索树深度,用两个bool数组记录节点的搜索状态,其中一个表示这个点是否曾经搜索过,减少重复搜索;另一个表示这个点是否在我们目前想要的环的路径上(可能有点抽象,就是说我们是否已经经过这个点并且走在这个点所连接的边上,搜索完是要回溯的,而前一个bool数组只是单纯记录这个点是否走过,不必回溯更改)
代码:
2938: [Poi2000]病毒
Time Limit: 1 Sec Memory Limit: 128 MB
Submit: 462 Solved: 240
[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
写在前面:给了三个样例的良心题
思路:
我们首先确定,如果可能,那么符合条件的字符串中一定有一个循环的字符串,如果不可能,那一定没有循环的字符串(因为循环的字符串是有循环节的,如果没有循环的字符串,那就意味着所有的循环节都不可以,那就是没有符合条件的了,反之亦如此)
然后偷个懒
zky:
首先我们把所有串建一个AC自动机
方便起见我们直接把fail指针合并到子结点
如果一个串能无限长,也就是说它可以在AC自动机上一直进行匹配但就是匹配不上
也就是说匹配指针不能走到val为1的结点,设这个点为x
即root..x是一个病毒串
那么fail指针指向x的y也不能走
因为root..x是root..y的一个后缀
处理出来判断有向图是否有环
dfs即可
建fail时把那些当前节点i没有的字符(相当于失配状态)都转移到fail[i]上去,之后我们只要从根上出发dfs一通乱走,如果途中遇到字符串结束的标记节点,就不走(因为一走不就相当于有这个病毒了吗),如果最后无路可走(怎么选都要经过标记节点)那就NiE了,但如果我们能走出一个没有标记节点的环,那就说明我们可以找到一个循环节使字符串合法且无限延长下去了
注意:
DFS时注意及时退出,减少搜索树深度,用两个bool数组记录节点的搜索状态,其中一个表示这个点是否曾经搜索过,减少重复搜索;另一个表示这个点是否在我们目前想要的环的路径上(可能有点抽象,就是说我们是否已经经过这个点并且走在这个点所连接的边上,搜索完是要回溯的,而前一个bool数组只是单纯记录这个点是否走过,不必回溯更改)
代码:
#include<bits/stdc++.h> using namespace std; int root=1,tot=1,n; int trie[30002][2],fail[30002]; bool num[30002],vis[30002],flag[30002]; char s[30002]; queue<int>q; void insert(char s[]) { int len=strlen(s),now=root; for (int i=0;i<len;i++) { if (!trie[now][s[i]-'0']) trie[now][s[i]-'0']=++tot; now=trie[now][s[i]-'0']; } num[now]=1; } void build() { int now,tmp; q.push(root); while (!q.empty()) { now=q.front(); q.pop(); for (int i=0;i<2;i++) if (trie[now][i]) { tmp=fail[now]; while (tmp&&!trie[tmp][i]) tmp=fail[tmp]; if (tmp&&now!=root) fail[trie[now][i]]=trie[tmp][i], num[trie[now][i]]+=num[trie[tmp][i]]; else fail[trie[now][i]]=root; q.push(trie[now][i]); } else { if (now==root) trie[now][i]=root; else trie[now][i]=trie[fail[now]][i]; } } } void dfs(int x) { if (flag[x]) {printf("TAK");exit(0);} if (num[x]||vis[x]) return; if (x!=root)vis[x]=1; flag[x]=1; for (int i=0;i<2;i++) dfs(trie[x][i]); flag[x]=0; } main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%s",s), insert(s); build(); dfs(root); printf("NIE"); }
相关文章推荐
- Linux相关知识点
- GDOI'2016市选day2 —— 2. 选举(ele)
- LeetCode:蛇形矩阵II(spiral matrix II)
- HDU 1686 Oulipo
- c++细节总结链接
- linux命令--telnet
- 粥可赛艇——重庆大学月赛
- 复利计算--结对
- NPOI2.0学习(一)
- java构造方法
- 毕业工作五年的总结和感悟(上)
- setDrawingCacheEnabled(boolean flag)的用法
- 【day0404】C++ 内联函数inline
- ubuntu14.04启动提示set_sw_state failed
- pdo 的配置与启用
- storm.yaml 配置项
- C++虚函数、虚函数的作用和使用方法
- 016 3Sum Closest
- MapReduce: WordCount的Eclipse实现
- SVG图标在移动端的应用