病毒
Problem description |
---|
二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。 例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。 任务: 请写一个程序: 读入病毒代码; 判断是否存在一个无限长的安全代码; 将结果输出。 |
Input |
第一行包括一个整数n,表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过30000。 |
Output |
输出一个单词: TAK——假如存在这样的代码; NIE——如果不存在 |
Sample Input |
3 01 11 00000 |
Sample Output |
NIE |
多串匹配,使用AC自动机。关于AC自动机自己也是靠这个题才学习理解的,还不是很熟悉,这里就不具体介绍了。只记录一个失配指针的产生过程的理解:
代码实现:
#include<iostream>
#include<string>
#include<queue>
using namespace std;
/*
使用数组存储字典树,fail数组,以及叶子节点数组,fail表示当前节点失配时跳转到的节点
visited数组记录从一个节点出发的一条路径上,一个节点是否被访问过
marked数组表示从一个节点出发是否能够产生环
*/
const int MAXN = 30010;
int Trie[MAXN][2], fail[MAXN], TrieNodes;
bool isVirus[MAXN], visited[MAXN], marked[MAXN];
void insert(string virus); //向Trie添加字符串
void build_fail(); //确定fail指针
bool search_loop(int u); //寻找是否存在环
int main(){
int n;
string virus;
cin>>n;
TrieNodes = 0;
for(int i=0;i<n;i++){
cin>>virus;
insert(virus);
}
build_fail();
bool flag = search_loop(0);
if(flag){
cout<<"TAK";
}else{
cout<<"NIE";
}
return 0;
}
//将病毒插入Trie
void insert(string virus){
int ptr, len;
len = virus.length();
ptr = 0;
for(int i=0;i<len;i++){
if(!Trie[ptr][virus[i]-'0']) Trie[ptr][virus[i]-'0'] = ++TrieNodes;
ptr = Trie[ptr][virus[i]-'0'];
}
isVirus[ptr] = true;
}
//DFS找到每个节点的fail指针
void build_fail(){
int i;
queue<int> que1;
//对于第一个字符,如果失配,就回到根节点0,这个节点可以看作是空串
for(int i=0;i<2;i++){
if(Trie[0][i]){
que1.push(Trie[0][i]);
fail[Trie[0][i]] = 0;
}
}
while(!que1.empty()){
int cur = que1.front();
que1.pop();
for(int i=0;i<2;i++){
int child = Trie[cur][i];
if(child){
que1.push(child);
fail[child] = Trie[fail[cur]][i];//见图
}
else{
Trie[cur][i] = Trie[fail[cur]][i];//跳转到另一个字符串进行匹配
}
//针对本题,总是选择失配的情况来找到非病毒的串,因此如果失配跳转到的位置是病毒,这个位置也是病毒
if(isVirus[fail[child]]) isVirus[child] = true;
}
}
}
//寻找环路
bool search_loop(int u){
visited[u]=1;
for(int i=0;i<2;i++){
if(visited[Trie[u][i]]){
return 1;
}
else if(!marked[Trie[u][i]]&&!isVirus[Trie[u][i]]){
marked[Trie[u][i]]=1;
if(search_loop(Trie[u][i])) return 1;
}
}
visited[u]=0;
//在这个AC自动机中不断的进行匹配,遇到子节点总是遇到病毒的情况,就不会继续了,所有可能路径都试过,就会退出
return 0;
}