编码

题目描述

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;
}
posted @ 2018-08-17 09:13  sciorz  阅读(485)  评论(0编辑  收藏  举报