- 题目描述
二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。 输入输出格式
- 输入格式:
在文本文件WIR.IN的第一行包括一个整数n(n\le 2000)(n≤2000),表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过30000。 - 输出格式:
在文本文件WIR.OUT的第一行输出一个单词:
TAK——假如存在这样的代码;
NIE——如果不存在。
- 输入格式:
思路
这种多串匹配的问题,直接就想到AC自动机。
不过这道题要变一下,转化为AC自动机上沿着trie上的边和fail指针走是否存在环。
然后就判环。
#include <bits/stdc++.h>
using namespace std ;
const int maxn = 3e5+5 ;
int n, m, h, s[maxn][2], tim[maxn], fail[maxn], fa[maxn], tot, p, q, ch[maxn] ;
char pat[maxn] ;
void insert() {
int len = strlen(pat+1) ;
int i, j, k ;
p = h ;
for ( i = 1 ; i <= len ; i ++ ) {
k = pat[i] - '0' ;
if ( s[p][k] ) {
p = s[p][k] ;
} else {
( s[p][k] = ++tot )[fa] = p ;
p = s[p][k] ;
ch[p] = pat[i] ;
}
}
++tim[p] ;
}
queue <int> Q ;
void calc() {
int i, j, k, l, r, x ;
Q.push(h) ;
while ( !Q.empty() ) {
x = Q.front() ;
Q.pop() ;
for ( i = 0 ; i < 2 ; i ++ )
if ( s[x][i] ) {
for ( p = x[fail] ; p && !s[p][i] ; p = p[fail] ) ;
s[x][i][fail] = p? s[p][i]:h ;
if ( s[x][i][fail][tim] ) s[x][i][tim] = 1 ;
Q.push(s[x][i]) ;
} else s[x][i] = s[x[fail]][i] ;
}
}
bool vis[maxn], inq[maxn] ;
void check ( int x ) {
printf ( "%d ch=%c fa=%d fail=%d tim=%d\n", x, ch[x], fa[x], fail[x], tim[x] ) ;
for ( int i = 0 ; i < 2 ; i ++ ) {
if ( s[x][i] ) check(s[x][i]) ;
}
}
void dfs ( int x ) {
if ( inq[x] ) return ;
inq[x] = 1 ;
vis[x] = 1 ;
int i, u ;
for ( i = 0 ; i < 2 ; i ++ ) {
if ( s[x][i] ) {
if ( s[x][i][vis] ) {
puts("TAK") ;
exit(0) ;
} else if ( !s[x][i][tim] ) dfs(s[x][i]) ;
}
}
vis[x] = 0 ;
}
int main() {
int i, j, k ;
scanf ( "%d", &n ) ;
h = ++tot ;
for ( i = 1 ; i <= n ; i ++ ) {
scanf ( "%s", pat+1 ) ;
insert() ;
}
calc() ;
vis[1] = 1 ;
dfs(1) ;
puts("NIE") ;
return 0 ;
}