分析
考虑trie+2sat
每次将?=0和?=1的分别插入
插入串时将这个点的选择状态和前缀的选择状态连关系边
注意串结束时建一个新点表示当前串
最后跑2sat即可
代码
#include<bits/stdc++.h>
using namespace std;
#define a(x) x<<1
#define b(x) x<<1|1
#define pb push_back
stack<int>a;
string s[500100];
vector<int>v[3000100];
int id[500100],siz[500100],n,m;
int trie[3000100][2],cnt,bel[3000100];
int low[3000100],dfn[3000100],is[3000100],T,sum;
inline bool cmp(int a,int b){return siz[a]<siz[b];}
inline void add(int x,int y){v[x].pb(y),v[y^1].pb(x^1);}
inline void tarjan(int x,int fa){
dfn[x]=low[x]=++T;
a.push(x);
is[x]=1;
for(int i=0;i<v[x].size();i++)
if(v[x][i]!=fa){
if(!dfn[v[x][i]]){
tarjan(v[x][i],x);
low[x]=min(low[x],low[v[x][i]]);
}else if(is[v[x][i]]){
low[x]=min(low[x],dfn[v[x][i]]);
}
}
if(low[x]==dfn[x]){
sum++;
while(1){
int u=a.top();
a.pop();
is[u]=0;
bel[u]=sum;
if(u==x)break;
}
}
return;
}
inline void ins(int i,int x){
int p=0,wh,la=0;
for(int j=0;j<siz[i];j++){
wh=(s[i][j]-'0');
if(!trie[p][wh])trie[p][wh]=++cnt;
la=p,p=trie[p][wh],add(x,a(p));
}
trie[la][wh]=++cnt;
add(x,b(cnt)),add(a(cnt),a(p));
return;
}
int main(){
int i,j,k,ok;
scanf("%d",&n);cnt=n;
for(i=1;i<=n;i++){
cin>>s[i];
id[i]=i;
siz[i]=s[i].length();
}
sort(id+1,id+n+1,cmp);
for(int _=1;_<=n;_++){
i=id[_],ok=0;
for(j=0;j<siz[i];j++)
if(s[i][j]=='?'){
s[i][j]='0',ins(i,a(i));
s[i][j]='1',ins(i,b(i));
ok=1;
break;
}
if(!ok){
ins(i,a(i));
add(b(i),a(i));
}
}
for(i=2;i<=(cnt<<1|1);i++)if(!dfn[i])tarjan(i,0);
for(i=1;i<=cnt;i++)
if(bel[a(i)]==bel[b(i)]){
puts("NO");
return 0;
}
puts("YES");
return 0;
}