编码
题目描述
Bob最近学习了一下二进制前缀编码的那一套理论。二进制编码是指一个由n个互不相同的二进制串s1,s2,...,sn构成的集合。而如果一套编码满足,对于任意的i≠j,si不是sj的前缀,那么我们称它为前缀编码。
Bob发现了一张上面写有n行二进制编码的纸。但这张纸年代久远,有些字迹已经模糊不清。幸运的是,每一行至多只会有一个模糊的字符。
Bob想知道这n行二进制编码是否有可能是一个前缀编码?
输入
输入第一行有一个整数n,表示编码的大小。
接下来n行,每行一个由0,1及?组成的字符串。保证每一行至多有一个?
输出
如果这n个二进制编码可能是前缀编码那么你需要输出“YES”,否则输出“NO”。
样例输入
4
00?
0?00
?1
1?0
样例输出
YES
分析
对于第i个字符串和第j个字符串,我们记\(i_0\)为把第i个字符串的问号变成0之后的字符串,\(i_1\)为把第i个字符串的问号变成0之后的字符串。
如果\(i_0\)是\(j_x\)的前缀,那么如果选择了\(i_0\)就必须选择\(j_{x\oplus1}\),如果选择了\(j_x\),就必须选择\(i_1\),这样就转化成了一个2-SAT问题。
我觉得难处理的是没有问号的字符串,问dy了好久...处理办法是,把这个点拆分为选择和不选,然后在2-SAT图里加上\(不选\rightarrow选\)这种边,非常机智了。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <cmath>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=500050;
int ch[maxn*2][2],tot,num;
int cur[maxn*2],bac[maxn*2];
vector<int> G[maxn*8];
void insert(char *s,int id) {
int u=0;
for(int i = 0; s[i]; ++i) {
int c=s[i]-'0';
if(s[i]=='?') c=(id&1);
if(!ch[u][c]) ch[u][c]=++tot;
u=ch[u][c];
}
if(!cur[u]) {
cur[u]=++num,bac[u]=++num;
G[id].push_back(cur[u]);
G[bac[u]].push_back(id^1);
}
else {
G[id].push_back(bac[u]);
G[cur[u]].push_back(id^1);
++num;
G[id].push_back(num),G[cur[u]].push_back(num),cur[u]=num;
++num;
G[num].push_back(id^1),G[num].push_back(bac[u]),bac[u]=num;
}
}
void dfs(int u,int pre1,int pre2) {
if(cur[u]) {
if(!pre1) pre1=cur[u],pre2=bac[u];
else {
G[cur[u]].push_back(pre2);
G[pre1].push_back(bac[u]);
++num;
G[pre1].push_back(num),G[cur[u]].push_back(num),pre1=num;
++num;
G[num].push_back(pre2),G[num].push_back(bac[u]),pre2=num;
}
}
if(ch[u][0]) dfs(ch[u][0],pre1,pre2);
if(ch[u][1]) dfs(ch[u][1],pre1,pre2);
}
int n,pre[maxn*8],lowlink[maxn*8],dfs_no,sccCnt,sccno[maxn*8];
stack<int> stk;
void dfs(int u) {
pre[u]=lowlink[u]=++dfs_no;
stk.push(u);
for(auto v:G[u]) {
if(!pre[v]) dfs(v),lowlink[u]=min(lowlink[u],lowlink[v]);
else if(!sccno[v]) lowlink[u]=min(lowlink[u],pre[v]);
}
if(pre[u]==lowlink[u]) {
++sccCnt;
while(1) {
int x=stk.top();stk.pop();
sccno[x]=sccCnt;
if(x==u) break;
}
}
}
char str[maxn];
int main(int argc, char const *argv[])
{
scanf("%d", &n);
num=n*2+2;
for (int i = 1; i <= n; ++i)
{
scanf("%s", str);
insert(str,i+i);
int len=strlen(str);
if(count(str,str+len,'?')) insert(str,i*2+1);
else G[i*2+1].push_back(i+i);
}
dfs(0,0,0);
for(int i = 1; i <= n; ++i) {
if(!pre[i*2]) dfs(i*2);
if(!pre[i*2+1]) dfs(i*2+1);
if(sccno[i*2]==sccno[i*2+1]) {
puts("NO");
return 0;
}
}
puts("YES");
return 0;
}