P2444 [POI2000]病毒
Posted snowy2002
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2444 [POI2000]病毒相关的知识,希望对你有一定的参考价值。
P2444 [POI2000]病毒
题目描述
二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例:
例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。
任务:
请写一个程序:
1.在文本文件WIR.IN中读入病毒代码;
?2.判断是否存在一个无限长的安全代码;
?3.将结果输出到文件WIR.OUT中。
输入输出格式
输入格式:
在文本文件WIR.IN的第一行包括一个整数n(n≤2000),表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过30000。
输出格式:
在文本文件WIR.OUT的第一行输出一个单词:
TAK——假如存在这样的代码;
NIE——如果不存在。
输入输出样例
这是一道非常有意思的AC自动机题目,首先我们可以根据读入的模式串把trie树给建好,然后把next数组搞出来,然后就GG了,这也没个文本串啊,这咋办啊?既然正着想不好办,我们可以考虑反过来想,如果有这样的一个字符串满足题目的要求不含有病毒代码,那么把这个字符串在trie树上跑回咋样呢?对啦,这个文本串一定不可以与任何一个模式串成功匹配,也就是说这个文本串会在trie树上跳来跳去,那么我们一定可以在trie树上找到一个环使得这个文本串会在这个环里来回跳。
看下面这个例子:
3
011
11
00000我们先把trie树建好,把next数组构造好,然后我们把文本串010101…跑一遍AC自动机,会发生什么呢?
不难看出,这个文本串会在上图中的0、1两个结点中来回跳,而且不能和任何一个模式串完全匹配。这样问题也就转化为了能否在trie树上利用next数组找到一个环使得这个环上不含有模式串的结束结点。看上去我们直接把所有模式串的结束结点打上标记然后直接dfs找环不就行了?貌似没有什么问题,但是这样做并不正确。
看下面的例子:
3
00
1
0100
我们继续把trie树和next数组给搞出来,然后进行dfs找环,但是这样会发生什么呢?我们会沿着上图中蓝色箭头所指的线路走,这样就会错误的判断为这个trie树上有环,但是显然这个树上是不存在环的,那么问题出在哪里呢?
对了,就是这个结点,根据next数组的含义,我们不难发现,只要这个结点的next链上含有模式串的结束结点,那么我们也应当把这个点打上模式串的结束标记,所以,我们还要把next链上存在模式串结束结点的点打上标记,这样就没有问题了。
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<string> 5 #include<cstring> 6 #include<map> 7 #include<queue> 8 #include<stack> 9 #include<algorithm> 10 #include<vector> 11 #define maxn 60005 12 using namespace std; 13 14 inline int read() 15 { 16 char c=getchar(); 17 int res=0,x=1; 18 while(c<‘0‘||c>‘9‘) 19 { 20 if(c==‘-‘) 21 x=-1; 22 c=getchar(); 23 } 24 while(c>=‘0‘&&c<=‘9‘) 25 { 26 res=res*10+(c-‘0‘); 27 c=getchar(); 28 } 29 return res*x; 30 } 31 32 int n,tot=1,pd; 33 int tree[maxn][15],nt[maxn],bo[maxn],dfn[maxn],vis[maxn]; 34 char a[maxn]; 35 queue<int>q; 36 37 void trie(char *s) 38 { 39 int len=strlen(s),u=1; 40 for(register int i=0;i<len;i++) 41 { 42 int c=s[i]-‘0‘; 43 if(!tree[u][c]) 44 { 45 tree[u][c]=++tot; 46 } 47 u=tree[u][c]; 48 } 49 bo[u]=1; 50 } 51 52 void bfs() 53 { 54 for(register int i=0;i<10;i++) 55 { 56 tree[0][i]=1; 57 } 58 nt[1]=0;q.push(1); 59 while(q.size()) 60 { 61 int u=q.front();q.pop(); 62 for(register int i=0;i<10;i++) 63 { 64 if(!tree[u][i]) 65 tree[u][i]=tree[nt[u]][i]; 66 else 67 { 68 int v=tree[u][i]; 69 q.push(v); 70 nt[v]=tree[nt[u]][i]; 71 } 72 } 73 } 74 } 75 76 void dfs(int x) 77 { 78 if(pd) return; 79 for(int i=0;i<=1;i++) 80 { 81 if(!vis[tree[x][i]]&&!bo[tree[x][i]]) 82 { 83 vis[tree[x][i]]=1; 84 dfs(tree[x][i]); 85 vis[tree[x][i]]=0; 86 } 87 else if(!bo[tree[x][i]]&&vis[tree[x][i]]) 88 { 89 pd=1; 90 return; 91 } 92 } 93 } 94 95 int main() 96 { 97 n=read(); 98 for(register int i=1;i<=n;i++) 99 { 100 scanf("%s",a); 101 trie(a); 102 } 103 bfs(); 104 vis[1]=1; 105 for(int i=2;i<=tot;i++) 106 { 107 int k=i,m=0; 108 while(k>1) 109 { 110 if(bo[k]) 111 m=k; 112 k=nt[k]; 113 } 114 k=i; 115 if(m==0) continue; 116 while(k>1) 117 { 118 if(k==m) break; 119 bo[k]=1; 120 k=nt[k]; 121 } 122 } 123 dfs(1); 124 if(pd) 125 { 126 printf("TAK "); 127 } 128 else 129 { 130 puts("NIE"); 131 } 132 return 0; 133 }
以上是关于P2444 [POI2000]病毒的主要内容,如果未能解决你的问题,请参考以下文章