我的题解(2)-病毒

先上题:

题目描述

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

输入

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

输出

你应在在文本文件WIN.OUT的第一行输出一个单词:
         TAK——假如存在这样的代码;
         NIE——如果不存在。

样例输入

3
01
11
00000

样例输出

NIE

首先,别考虑直接输答案 鄙视

来说说思路:
trie树显然不行(关于trie树,就不多说了)。

So-用trie图。trie图可以使用后缀进行查找,让查找在图中畅通无阻。
重点是:如何判断可以出现无限长的代码呢?
想想,当图中出现 且环中没有危险节点时,我们就按此环循环,可出现无限长代码。
使用拓扑排序可判断环(当前节点的入度都≥1时,有环。x的入度就是指向x节点的节点的个数)。

具体步骤:
1.创建trie树;
2.连接虚边,创建trie图;
3.进行搜索,消掉入度为零的点,减掉 入度为零的点指向的点的一个入度.

代码(请不要管注释,为调试信息-我的代码错了13次 敲打):

#include<bits/stdc++.h>
using namespace std;
int n,trie[30001][2],nodenum=1,danger[30001],fail[30001],que[30001],head,tail,indeg[30001];
char s[30001];
int build_trie(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        int now=1;
        for(int j=1;j<=strlen(s+1);j++){
            if(danger[now]) break;
            int t=s[j]-48;
            if(trie[now][t]==0)
                trie[now][t]=++nodenum;
            now=trie[now][t];
        }
        danger[now]=1;
    }
}
void build_acm(int root){
    nodenum=1;
    fail[root]=root;
    head=1,tail=0;
    for(int i=0;i<=1;i++){
        if(trie[root][i]!=0){
            fail[trie[root][i]]=root;
            que[++tail]=trie[root][i];
            indeg[trie[root][i]]++;
        }
        else trie[root][i]=root,indeg[1]++;
    }
    int u;
    while(head<=tail){
        u=que[head++];
        if(danger[u]) continue;
        nodenum++;
        for(int i=0;i<=1;i++){
            int v=trie[u][i];
            if(v==0){
                trie[u][i]=trie[fail[u]][i];
                indeg[trie[u][i]]++;
                continue;
            }
            fail[v]=trie[fail[u]][i];
            que[++tail]=v;
            danger[v]=danger[v]|danger[fail[v]];
            indeg[trie[u][i]]++;
        }
    }
}
void toposort(){
    if(indeg[1]>0){
        printf("TAK");
        return;
    }
    memset(que,0,sizeof(que));
    int cnt=1;
    head=1,tail=0;
    que[++tail]=1;
//  for(int i=1;i<=nodenum;i++) cout<<indeg[i]<<' '<<danger[i]<<endl;
//  for(int i=1;i<=nodenum;i++) printf("node:%d,fail:%d,indeg:%d,lc:%d,rc:%d\n",i-1,fail[i]-1,indeg[i],trie[i][0]-1,trie[i][1]-1);
//  cout<<endl;
    while(head<=tail){
        int num=que[head++];
        for(int j=0;j<=1;j++){
            if(danger[trie[num][j]]) continue;
            indeg[trie[num][j]]--;
            if(!indeg[trie[num][j]]) cnt++,que[++tail]=trie[num][j];
        }
    }
//    for(int i=1;i<=nodenum;i++) printf("node:%d,fail:%d,indeg:%d,lc:%d,rc:%d,danger:%d\n",i-1,fail[i]-1,indeg[i],trie[i][0]-1,trie[i][1]-1,danger[i]);
//    cout<<cnt<<' '<<nodenum;
    if(cnt<nodenum) printf("TAK");
    else printf("NIE");
}
int main(){
    build_trie();
    build_acm(1);
    toposort();
    return 0;
}
如果有问题,请告诉我哟~
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值