@gym - [emailprotected] Binary Code
目录
@[email?protected]我们称一组字符串是 “前缀码”,当且仅当不存在一个字符串为另一个字符串的前缀。 现在给定 n 个 01 字符串,其中有些字符串存在最多一个的未知字符。 问是否能将未知字符替换为 0 或 1,使得这 n 个字符串构成 “前缀码”。 Input Output Examples binary.in @[email?protected]含有问号的串有两种状态,且串 s 选定某个状态时,会导致另一些串只能选择与 s 没有前缀关系的状态。这可以使我们联想到 2-sat。 这样建图是 O(n^2) 的,考虑优化建图。 我们建出 trie 后,将 trie 拆成两棵 T1,T2,T1 方向全部朝根结点,T2 方向全部朝叶结点。 最后一个问题:对于长得完全一样的 x1,x2,...,xp,我们不能够随便乱连,否则可能会导致 xi -> xi‘ 的不合法连边。 @accepted [email?protected]#include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; const int MAXN = 1000000; const int MAXV = 5*MAXN; vector<int>G[MAXV + 5]; int n,m,ncnt; void addedge(int u,int v) {G[u].push_back(v);} int ch[2][MAXN + 5]; vector<int>vec[MAXV + 5]; void insert(char *S,int lenS,int id) { int nw = 0; for(int i=0;i<lenS;i++) { if( ch[S[i] - '0'][nw] == 0 ) ch[S[i] - '0'][nw] = (++ncnt); nw = ch[S[i] - '0'][nw]; } vec[nw].push_back(id); } void build_trie_edge(int rt,int fa) { if( !rt ) return ; addedge(fa + 2*n + 0*(ncnt + 1),rt + 2*n + 0*(ncnt + 1)); addedge(fa + 2*n + 2*(ncnt + 1),rt + 2*n + 0*(ncnt + 1)); addedge(rt + 2*n + 1*(ncnt + 1),fa + 2*n + 1*(ncnt + 1)); addedge(rt + 2*n + 2*(ncnt + 1),fa + 2*n + 1*(ncnt + 1)); for(int i=0;i<vec[rt].size();i++) { addedge(rt + 2*n + 0*(ncnt + 1),vec[rt][i]^1); addedge(rt + 2*n + 1*(ncnt + 1),vec[rt][i]^1); addedge(vec[rt][i],rt + 2*n + 2*(ncnt + 1)); } build_trie_edge(ch[0][rt],rt); build_trie_edge(ch[1][rt],rt); } void build_node_edge(int rt) { if( !rt ) return ; if( vec[rt].size() >= 2 ) { int lst = vec[rt][0]^1; for(int i=1;i<vec[rt].size();i++) { addedge(vec[rt][i],lst); if( i + 1 == vec[rt].size() ) break; m++; addedge(m,lst); addedge(m,vec[rt][i]^1); lst = m; } lst = vec[rt][vec[rt].size() - 1]^1; for(int i=int(vec[rt].size())-2;i>=0;i--) { addedge(vec[rt][i],lst); if( i == 0 ) break; m++; addedge(m,vec[rt][i]^1); lst = m; } } build_node_edge(ch[0][rt]); build_node_edge(ch[1][rt]); } int tid[MAXV + 5],low[MAXV + 5],num[MAXV + 5],stk[MAXV + 5]; int tp,tot,dcnt; void dfs(int x) { stk[++tp] = x; tid[x] = low[x] = (++dcnt); for(int i=0;i<G[x].size();i++) { int p = G[x][i]; if( !tid[p] ) dfs(p),low[x] = min(low[x],low[p]); else if( !num[p] ) low[x] = min(low[x],tid[p]); } if( low[x] >= tid[x] ) { tot++; while( tp && tid[stk[tp]] >= tid[x] ) { int t = stk[tp--]; num[t] = tot,vec[tot].push_back(t); } } } bool tag[MAXV + 5]; void solve() { for(int i=1;i<=tot;i++) { for(int j=0;j<vec[i].size();j++) { int x = vec[i][j]; for(int k=0;k<G[x].size();k++) if( tag[num[G[x][k]]] ) tag[i] = true; } if( !tag[i] ) { for(int j=0;j<vec[i].size();j++) { int x = vec[i][j]; if( x < 2*n ) tag[num[x^1]] = true; } } } } char str[MAXN + 5]; int len[MAXN + 5]; int main() { freopen("binary.in","r",stdin); freopen("binary.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",str + len[i-1]); len[i] = len[i-1] + strlen(str + len[i-1]); } for(int i=1;i<=n;i++) { int pos = -1; for(int j=len[i-1];j<len[i];j++) if( str[j] == '?' ) pos = j; if( pos == -1 ) { addedge((i-1)<<1|1,(i-1)<<1|0); insert(str + len[i-1],len[i] - len[i-1],(i-1)<<1|0); } else { str[pos] = '0',insert(str + len[i-1],(i-1)<<1|0); str[pos] = '1',(i-1)<<1|1); str[pos] = '?'; } } m = 2*n + 3*(ncnt + 1); build_trie_edge(ch[0][0],0),build_trie_edge(ch[1][0],0); build_node_edge(ch[0][0]),build_node_edge(ch[1][0]); for(int i=0;i<=ncnt;i++) vec[i].clear(); for(int i=0;i<=m;i++) if( !tid[i] ) dfs(i); for(int i=0;i<n;i++) if( num[i<<1] == num[i<<1|1] ) { puts("NO"); return 0; } puts("YES"); solve(); for(int i=1;i<=n;i++) for(int j=len[i-1];j<len[i];j++) if( str[j] == '?' ) str[j] = tag[num[(i-1)<<1]] + '0'; for(int i=1;i<=n;i++) { for(int j=len[i-1];j<len[i];j++) putchar(str[j]); puts(""); } } @[email?protected]一开始 RE 了,非常懵逼。 另外,原来 tarjan 求出来的强连通的编号就是天然的拓扑序。 2-sat 用拓扑排序求出来的方案数是非常“任意”的——即它既没有字典序也没有任何已知规律。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |