Vigenère密码

来源  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密码

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 }

 

当然,简单不费脑力的做法也可以实现,即:直接手工或者写代码构造出一个二维数组存储的运算表格,然后解密时用**确定列,用密文确定行即可得到明文。