來源 NOIP2012復賽 提高組 第一題
- 描述
-
16世紀法國外交家Blaise de Vigenère設計了一種多表密碼加密算法——Vigenère密碼。Vigenère密碼的加密解密算法簡單易用,且破譯難度比較高,曾在美國南北戰爭中為南軍所廣泛使用。
在密碼學中,我們稱需要加密的信息為明文,用M表示;稱加密后的信息為密文,用C表示;而密鑰是一種參數,是將明文轉換為密文或將密文轉換為明文的算法中輸入的數據,記為k。 在Vigenère密碼中,密鑰k是一個字母串,k=k1k2…kn。當明文M=m1m2…mn時,得到的密文C=c1c2…cn,其中ci=mi®ki,運算®的規則如下表所示:
Vigenère加密在操作時需要注意:
1. ®運算忽略參與運算的字母的大小寫,並保持字母在明文M中的大小寫形式;
2. 當明文M的長度大於密鑰k的長度時,將密鑰k重復使用。
例如,明文M=Helloworld,密鑰k=abc時,密文C=Hfnlpyosnd。
明文 |
H |
e |
l |
l |
o |
w |
o |
r |
l |
d |
密鑰 |
a |
b |
c |
a |
b |
c |
a |
b |
c |
a |
密文 |
H |
f |
n |
l |
p |
y |
o |
s |
n |
d |
- 輸入
-
輸入共2行。
第一行為一個字符串,表示密鑰k,長度不超過100,其中僅包含大小寫字母。第二行為一個字符串,表示經加密后的密文,長度不超過1000,其中僅包含大小寫字母。
對於100%的數據,輸入的密鑰的長度不超過100,輸入的密文的長度不超過1000,且都僅包含英文字母。 - 輸出
- 輸出共1行,一個字符串,表示輸入密鑰和密文所對應的明文。
- 樣例輸入
-
CompleteVictory Yvqgpxaimmklongnzfwpvxmniytm
- 樣例輸出
-
Wherethereisawillthereisaway
分析:
觀察運算®的規則表格發現該表格是左下和右上對稱的,也就是說:可以認為加密時行標題是明文、列標題是密鑰;也可以認為加密時行標題是密鑰,列標題是明文。這里我們把行標題看做明文,列標題看做密鑰。
然后還可以發現:加解密運算規則表格的每一列其實是把行標題加上一個對應值就可以轉換出來的。例如:表格中的第0列(即密鑰為A的那一列)是把行標題各字母分別加0轉換而來;表格中的第1列(密鑰為B的那一列)是把行標題各字母分別加1轉換而來;……以此類推,我們可以發現一個規律:
明文字母 = (密文字母-A - (密鑰字母 - A) + 26 ) mod 26 + A
上面公式里面加或減A是為了求得對應的密文字母或密鑰字母在26個英文字母表當中的序號(0~25),所以需要討論密文字母和密鑰字母的大小寫然后再決定是減去(或加上)大寫A或小寫a。具體的討論可以看代碼。
1 #include <stdio.h> 2 int main() 3 { 4 char k[102],c[1002]; 5 int i,j; 6 char temp; 7 8 scanf("%s",k); 9 getchar(); 10 scanf("%s",c); 11 12 for(i=0,j=0;c[j]!='\0';j++) 13 { 14 15 if(c[j]>='A'&&c[j]<='Z') 16 { 17 if(k[i]>='A'&&k[i]<='Z') 18 temp=(c[j]-'A'-(k[i]-'A')+26)%26+'A'; 19 else temp=(c[j]-'A'-(k[i]-'a')+26)%26+'A'; 20 } 21 else 22 { 23 if(k[i]>='A'&&k[i]<='Z') 24 temp=(c[j]-'a'-(k[i]-'A')+26)%26+'a'; 25 else temp=(c[j]-'a'-(k[i]-'a')+26)%26+'a'; 26 } 27 printf("%c",temp); 28 i++; 29 if(k[i]=='\0') i=0; 30 } 31 return 0; 32 }
當然,簡單不費腦力的做法也可以實現,即:直接手工或者寫代碼構造出一個二維數組存儲的運算表格,然后解密時用密鑰確定列,用密文確定行即可得到明文。