原文鏈接:http://yeefan.tk/blog/haoi-encrypt/
資訊的明文是由0利1組成的非空序列。但在網絡通信中,為了資訊的安全性,常對明文進行加密,用密文進行傳輸。密文是由0、1和若干個密碼字母組成,每個密碼字母代表不同的01串,例如,密文二011a0bf00a01。密碼破譯的關鍵是確定每個密碼的含義。
經過長期統計分析,現在知道了每個密碼的固定長度,如今,我方又截獲了敵方的兩段密文S1和S2,並且知道S1二S2,即兩段密文代表相同的明文。你的任務是幫助情報人員對給定的兩段密文進行分析,看一看有多少種可能的明文。
【輸入文件】
第1行: S1 (第1段密文)
第2行: S2 (第2段密文)
第3行: N (密碼總數, N<=26)
第4—N+3行: 字母i 長度i (密碼用小寫英文字母表示, 密碼長度<=100)
【輸出文件】
M(表示有M種可能的明文)
【輸入輸出樣例】
encrypt.in
100ad1
cc1
4
a 2
d 3
c 4
b 50
encrypt.out
2
【約束條件】
明文的長度<=10000
【分析】
既然每個密文代表一段明文,那我們可以把密文展開
如樣例的:
100ad1
cc1
4
a 2
d 3
c 4
b 50
我們就可以展開成
1 0 0 a1 a2 d1 d2 d3 1
c1 c2 c3 c4 c1 c2 c3 c4 1
那麼問題就成了在這麼一串數中找出有多少個數不定集合.
即c1代表1 c2代表0 c3代表0
但是 c4和a1共同代表什麼呢,這是問題的關鍵,這就用到了並查集:
首先 1 0 a1 a2…d2 d3都是獨立的集合
從i=1開始向右掃描
先把1所在的集合和c1所在的集合歸成一類,
0所在的集合和c2所在的集合歸成一類
….
最後一定是
代表0的字母有一個集合
代表1的字母有一個集合
不能確定代表什麼的有x個集合
因為每個集合代表的數字要麼是1要麼是0有兩種情況
所以兩個集合有4種情況
三個集合有8種情況
x個集合有2^x種情況
所以我們只需要用並查集算出有幾個非0或1的集合,再累乘後輸出即可
注意:有一個密文既對應0也對應1,這種情況構不成任何正確的明文,所以應當輸出0。
【我的代碼】
#include <cstdio> #include <cstdlib> #include <iostream> #include <cstring> using namespace std; int num[30]; const char base='a'-1; const int MAXN=3000; int Letter[30];//每個密碼的密碼長度 int Num[30][102]; char S1[105]; char S2[105]; int N;//密碼總數 int T=0;//不定集合的數量 int Sum=0;//最終結果 int M=0; int DealA[MAXN]={0}; int DealB[MAXN]={0}; int Num0; int Num1; int Len=0; class Node { public: int parent; int count; }UFS[MAXN]; void UFS_Init(int i) { UFS[i].count=1; UFS[i].parent=i; } int UFS_Find(int x) { int i=x; while(i!=UFS[i].parent) i=UFS[i].parent; int j=x; while(j!=i) { int tmp=UFS[j].parent; UFS[j].parent=i; j=tmp; } return i; } void UFS_Merge(int x,int y) { x=UFS_Find(x); y=UFS_Find(y); if(x==y) return; if(UFS[x].count>UFS[y].count) { UFS[y].parent=x; UFS[x].count+=UFS[y].count; } else { UFS[x].parent=y; UFS[y].count+=UFS[x].count; } } void init() { scanf("%s\n%s\n",&S1,&S2); scanf("%d\n",&N); memset(Letter,0,sizeof(Letter)); char cTmp; int iTmp; for (int i=1;i<=N;i++) { scanf("%c %d\n",&cTmp,&iTmp); Letter[cTmp-base]=iTmp; } for(int i=1;i<=26;i++) { for (int j=1;j<=Letter[i];j++) { M++; Num[i][j]=M; UFS_Init(M); } } M++; Num0=M; UFS_Init(M); M++; Num1=M; UFS_Init(M); int top=0; for (unsigned int i=0;i<strlen(S1);i++) { if(isalpha(S1[i])) { for (int j=1;j<=Letter[ S1[i]-base ]; j++) { top++; DealA[top]=Num[S1[i]-base][j]; } } else { if(S1[i]=='0') { top++; DealA[top]=Num0; } else { top++; DealA[top]=Num1; } } } Len=top; top=0; for (unsigned int i=0;i<strlen(S2);i++) { if(isalpha(S2[i])) { for (int j=1;j<=Letter[ S2[i]-base ]; j++) { top++; DealB[top]=Num[S2[i]-base][j]; } } else { if(S2[i]=='0') { top++; DealB[top]=Num0; } else { top++; DealB[top]=Num1; } } } } int work() { for(int i=1;i<=Len;i++) { UFS_Merge(DealA[i],DealB[i]); } if(UFS_Find(Num0)==UFS_Find(Num1)) { return 0; } bool Used[MAXN]; memset(Used,0,sizeof(Used)); int Find_Num0=UFS_Find(Num0); int Find_Num1=UFS_Find(Num1); for(int i=1;i<=Len;i++) { int tmp=UFS_Find(DealA[i]); if(tmp==Find_Num0 || tmp==Find_Num1 || Used[tmp]) continue; T++; Used[tmp]=true; } for(int i=1;i<=Len;i++) { int tmp=UFS_Find(DealB[i]); if(tmp==Find_Num0 || tmp==Find_Num1 || Used[tmp]) continue; T++; Used[tmp]=true; } Sum=1; for (int i=1;i<=T;i++) Sum*=2; return Sum; } int main() { freopen("encrypt.in","r",stdin); freopen("encrypt.out","w",stdout); init(); printf("%d\n",work()); return 0; }