题目大意
给定 n 个01串,有一些串中有一个位置未确定。现在要判断,能否找到一种方案,使得任意一个串都不是其他任一串的前缀。
Data Constraint
题解
考虑用Trie建图,跑2-SAT
将所有可能的串加入一个Trie里,每个点向它根路径上经过的点的另一个连边,表示这两种不能同时选。
然后Tarjan判一下就好了。
时间复杂度: O(n)
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std ;
#define N 5000000 + 10
#define M 500000 + 10
struct Trie {
int Son[2] , fa ;
int In , Out , use ;
} T[2*M] ;
vector < int > P[2*M] ;
char S[N] ;
bool ok = 1 ;
bool vis[N] , flag[N] ;
int Node[5*N] , Next[5*N] , Head[N] , tot ;
int DFN[N] , LOW[N] , Sta[N] , Bel[N] , Time ;
int from[N] , Pre[N] , Suf[N] ;
int n , Cnt , Col , Amo , Num = 1 ;
void link( int u , int v ) {
Node[++tot] = v ;
Next[tot] = Head[u] ;
Head[u] = tot ;
}
int Rev( int x ) {
if ( x > 2 * n ) return 0 ;
return x > n ? x - n : x + n ;
}
void Insert( int len , int ID ) {
int now = 1 ;
for (int i = 1 ; i <= len ; i ++ ) {
int c = S[i] - '0' ;
if ( !T[now].Son[c] ) {
T[now].Son[c] = ++ Num ;
T[Num].fa = now ;
}
now = T[now].Son[c] ;
}
if ( !T[now].use ) T[now].use = ++ Amo ;
P[T[now].use].push_back( ID ) ;
if ( !T[now].In ) T[now].In = ++ Cnt ;
if ( !T[now].Out ) T[now].Out = ++ Cnt ;
link( T[now].In , Rev(ID) ) ;
link( ID , T[now].Out ) ;
from[ID] = now ;
}
void Build( int st ) {
if ( vis[st] ) return ;
vis[st] = 1 ;
int siz = P[T[st].use].size() ;
Pre[0] = Suf[siz+1] = 0 ;
for (int i = 1 ; i <= siz ; i ++ ) {
int now = Rev(P[T[st].use][i-1]) ;
Pre[i] = ++ Cnt ;
if ( Pre[i-1] > 0 ) link( Pre[i] , Pre[i-1] ) ;
link( Pre[i] , now ) ;
}
for (int i = siz ; i >= 1 ; i -- ) {
int now = Rev(P[T[st].use][i-1]) ;
Suf[i] = ++ Cnt ;
if ( Suf[i+1] > 0 ) link( Suf[i] , Suf[i+1] ) ;
link( Suf[i] , now ) ;
}
for (int i = 1 ; i <= siz ; i ++ ) {
int now = P[T[st].use][i-1] ;
if ( Pre[i-1] > 0 ) link( now , Pre[i-1] ) ;
if ( Suf[i+1] ) link( now , Suf[i+1] ) ;
}
int last = st ;
for (int x = T[st].fa ; x != 1 ; x = T[x].fa ) {
if ( T[x].In == 0 ) continue ;
link( T[last].Out , T[x].In ) ;
link( T[x].Out , T[last].In ) ;
}
}
void Tarjan( int x ) {
Sta[++Sta[0]] = x ;
flag[x] = 1 ;
DFN[x] = LOW[x] = ++ Time ;
for (int p = Head[x] ; p ; p = Next[p] ) {
if ( !DFN[Node[p]] ) {
Tarjan( Node[p] ) ;
LOW[x] = min( LOW[x] , LOW[Node[p]] ) ;
} else if ( flag[Node[p]] ) LOW[x] = min( LOW[x] , DFN[Node[p]] ) ;
if ( !ok ) return ;
}
if ( DFN[x] == LOW[x] ) {
Col ++ ;
while ( Sta[0] ) {
int now = Sta[Sta[0]] ;
Bel[now] = Col ;
flag[now] = 0 ;
Sta[0] -- ;
if ( Bel[now] == Bel[Rev(now)] ) { ok = 0 ; return ; }
if ( now == x ) break ;
}
}
}
bool Check() {
for (int i = 1 ; i <= 2 * n ; i ++ ) {
if ( !DFN[i] ) Tarjan( i ) ;
if ( !ok ) return 0 ;
}
return 1 ;
}
int main() {
freopen( "code.in" , "r" , stdin ) ;
freopen( "code.out" , "w" , stdout ) ;
scanf( "%d" , &n ) ;
Cnt = 2 * n ;
for (int i = 1 ; i <= n ; i ++ ) {
scanf( "%s" , S + 1 ) ;
int len = strlen( S + 1 ) , wz = 0 ;
for (int j = 1 ; j <= len ; j ++ ) {
if ( S[j] == '?' ) wz = j ;
}
if ( !wz ) Insert( len , i ) , link( n + i , i ) ;
else {
from[i] = from[n+i] = 1 ;
S[wz] = '0' ;
Insert( len , i ) ;
S[wz] = '1' ;
Insert( len , n + i ) ;
}
}
for (int i = 1 ; i <= 2 * n ; i ++ ) {
if ( !from[i] ) continue ;
Build( from[i] ) ;
}
if ( Check() ) printf( "YES\n" ) ;
else printf( "NO\n" ) ;
return 0 ;
}
以上.