算法实验-病毒(AC自动机)

这篇博客介绍了如何利用AC自动机解决一个计算机科学问题,即判断是否存在一个无限长的、不包含已知病毒代码段的二进制字符串。文章详细讲解了AC自动机的构建过程,包括插入病毒代码、确定失败指针和搜索环路的算法,并给出了C++代码实现。通过对二进制串的处理,判断是否存在避免病毒代码的安全串,从而确定安全的无限长二进制序列是否可能。
摘要由CSDN通过智能技术生成

病毒

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值