题目描述
Bob 最新学习了一下二进制前缀编码的那一套理论。二进制编码是指一个由 n nn 个互不相同的二进制串 s1,s2,…,sn s_1, s_2, \ldots, s_ns1,s2,…,sn 构成的集合。而如果一套编码理论满足,对于任意的 i≠j i \neq ji≠j,si s_isi 不是 sj s_jsj 的前缀,那么我们称它为前缀编码。
Bob 发现了一张上面写有 n nn 行二进制编码的纸,但这张纸年代久远,有些字迹已经模糊不清。幸运的是,每一行至多只会有一个模糊的字符。
Bob 想知道这 n nn 行二进制编码是否有可能是一个前缀编码?
输入
第一行一个整数 n nn,表示编码的大小。
接下来 n nn 行,每行一个由0、1及?组成的字符串。保证每一行至多有一个?。
输出
如果这 n nn 个二进制编码可能是前缀编码,输出YES,否则输出NO。
样例输入
4
00?
0?00
?1
1?0
样例输出
YES
提示
本题采用捆绑测试。
你需要通过一个子任务内的所有测试点才能得到该子任务的分数。
子任务 | 分值 | n nn | 字符串总长 |
---|---|---|---|
1 | 20 | ≤10 \leq 10≤10 | ≤1000 \leq 1000≤1000 |
2 | 30 | ≤1000 \leq 1000≤1000 | ≤500000 \leq 500000≤500000 |
3 | 50 | ≤500000 \leq 500000≤500000 | ≤500000 \leq 500000≤500000 |
#include<bits/stdc++.h>
using namespace std;
template<class T>inline void init(T&x){
x=0;char ch=getchar();bool t=0;
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
if(t) x=-x;return;
}
#define TR(a) ((a)<<1|1)
#define FA(a) ((a)<<1)
const int N=5e5+10;
const int MAXN=3e6+10;
int son[MAXN][2];
char S[N];
char *s[N];int len[N],id[N];
int n,cnt=0;
inline bool cmp(int i,int j){return len[i]<len[j];}
struct edge{
int to,next;
}a[MAXN];
int head[MAXN],cur=0,dfn[MAXN],low[MAXN],bel[MAXN],stk[MAXN],top=0,vis[MAXN],I=0,bcc;
inline void add(int x,int y){a[++cur]=(edge){y,head[x]};head[x]=cur;}
void tarjan(int u){
dfn[u]=low[u]=++I,stk[++top]=u,vis[u]=1;
for(int v,i=head[u];i;i=a[i].next){
v=a[i].to;
if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
else if(vis[v]) low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]) {
int v;++bcc;
do{v=stk[top--];vis[v]=0;bel[v]=bcc;if(bel[v]==bel[v^1]) {puts("NO"),exit(0);}}while(v!=u);
}
return;
}
void Insert(char*s,int len,int Jud){
int p=0,las=0;
for(int i=1;i<=len;++i) {
int c=s[i]-'0';
las=p;
if(son[p][c]) p=son[p][c];
else {
son[p][c]=++cnt;
if(p) add(TR(p),TR(cnt)),add(FA(cnt),FA(p));
p=cnt;
}
}
++cnt;//新建点以防止出现多个串共用了一个点的情况 , 新建的点是要考虑当前点的!
add(Jud,FA(p)),add(TR(p),Jud^1);
add(TR(p),TR(cnt)),add(FA(cnt),FA(p));
add(Jud,TR(cnt)),add(FA(cnt),Jud^1);
son[las][(int)(son[las][1]==p)]=cnt;
}
int main()
{
init(n);int now=1;cnt=n;// n 个串选择什么
for(int i=1;i<=n;++i){
scanf("%s",S+now);
len[i]=strlen(S+now);
s[i]=&S[now-1];now+=len[i];
id[i]=i;
}sort(id+1,id+1+n,cmp);
for(int i=1;i<=n;++i) {
int t=id[i];int j=0;
for(j=1;j<=len[t];++j) {
if(s[t][j]=='?') {// 有问号
s[t][j]='0';Insert(s[t],len[t],FA(t));//填 0
s[t][j]='1';Insert(s[t],len[t],TR(t));//填 1
break;
}
}
if(j>len[t]) {add(TR(t),FA(t));Insert(s[t],len[t],FA(t));}
}
for(int i=0;i<=cnt;++i) if(!dfn[i]) tarjan(i);
puts("YES");
return 0;
}