每个串只有一个?
,?
还只能填0
或者1
,不难想到2-SAT求解。
一个很暴力的想法是枚举?
填0
或者1
,然后对所有可能的前缀连边。这样边数是(O(n^2))的,需要优化。
看到前缀不难想到Trie树。将所有串的所有可能形态填入Trie树中,然后使用前缀后缀优化2-SAT连边的方式优化连边。
具体来说对于每一个串开两个点表示?
填0
还是1
,对于Trie树上每一个串的结束节点也开两个点,表示这个点及其所有前缀中是否存在已经选过的串。
连边考虑一些互为前缀的串。设串为(s_1,s_2,s_3,...,s_k),第(i)个串在Trie树上的节点的(01)变量为(bool[i][0/1]),第(i)个节点对应串的(01)变量为(belong[i][0/1])(为了好描述,这里定义的(belong[i][0/1])表示第(i)个串填入0
或1
之后是否得到当前串,是为(1))
那么有边
(belong[i][1] ightarrow bool[i][1])
(bool[i][0] ightarrow bool[i -1][0])
(bool[i][1] ightarrow bool[i + 1][1])
(bool[i][0] ightarrow belong[i][0])
(bool[i][1] ightarrow belong[i + 1][0])
这些边可以在建Trie的过程中直接建。记得要建逆否命题的边。然后跑一遍缩点就行了
细节:①开始要将字符串按长度从小到大排序,才可以保证上面方法的正确性;②可能存在某些串相等,建Trie的时候要特别注意。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<queue>
#include<vector>
#define INF 0x3f3f3f3f
//This code is written by Itst
using namespace std;
const int MAXN = 3e6 + 3;
struct Edge{
int end , upEd;
}Ed[MAXN << 3];
int head[MAXN] , N , cntN = 1 , cntEd;
inline void addEd(int a , int b){
Ed[++cntEd] = (Edge){b , head[a]};
head[a] = cntEd;
}
namespace Trie{
int ch[MAXN][2] , ind[MAXN] , cnt = 1;
void insert(string s , int bl){
int cur = 1 , up = 0;
for(auto c : s){
if(!ch[cur][c - '0'])
ch[cur][c - '0'] = ++cnt;
cur = ch[cur][c - '0'];
if(ind[cur]) up = ind[cur];
}
cntN += 2;
addEd(bl , cntN); addEd(cntN ^ 1 , bl ^ 1);
if(up){
addEd(up , cntN); addEd(cntN ^ 1 , up ^ 1);
addEd(up , bl ^ 1); addEd(bl , up ^ 1);
}
ind[cur] = cntN;
}
}
using Trie::insert;
int stk[MAXN] , dfn[MAXN] , low[MAXN] , in[MAXN];
int top , ts , cntSCC;
bool vis[MAXN] , ins[MAXN];
void pop(int x){
++cntSCC;
do{
in[stk[top]] = cntSCC;
ins[stk[top]] = 0;
}while(stk[top--] != x);
}
void tarjan(int x , int p){
vis[x] = ins[x] = 1;
stk[++top] = x;
dfn[x] = low[x] = ++ts;
for(int i = head[x] ; i ; i = Ed[i].upEd){
if(!vis[Ed[i].end]) tarjan(Ed[i].end , x);
else if(!ins[Ed[i].end]) continue;
low[x] = min(low[x] , low[Ed[i].end]);
}
if(dfn[x] == low[x]) pop(x);
}
vector < string > str;
bool cmp(string a , string b){return a.size() < b.size();}
int main(){
#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
//freopen("out","w",stdout);
#endif
cin >> N;
for(int i = 1 ; i <= N ; ++i){
string s;
cin >> s;
str.push_back(s);
}
sort(str.begin() , str.end() , cmp);
for(auto t : str){
cntN += 2;
int nd = cntN , pos = t.find('?');
if(pos != string::npos){
t[pos] = '0';
insert(t , nd - 1);
t[pos] = '1';
insert(t , nd);
}
else{
insert(t , nd);
addEd(nd - 1 , nd);
}
}
for(int i = 2 ; i <= cntN ; ++i)
if(!vis[i]) tarjan(i , 0);
for(int i = 2 ; i <= cntN ; i += 2)
if(in[i] == in[i + 1])
return puts("NO") , 0;
puts("YES");
return 0;
}