描述
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
【样例分析】
- 密钥(key):
CompleteVictory
- 密文(cipherText):
Yvqgpxaimmklongnzfwpvxmniytm
转换为明文的详细步骤如下:
首先,将密钥转换为全大写:COMPLETEVICTORY
。
接下来,我们逐个字符地解密密文。每个字符的解密过程如下:
密文字符 | 密钥字符 | 解密操作和结果 | 明文字符 |
---|---|---|---|
Y | C | (Y - C) % 26 = W | W |
v | O | (v - O) % 26 = h | h |
q | M | (q - M) % 26 = e | e |
g | P | (g - P) % 26 = r | r |
p | L | (p - L) % 26 = e | e |
x | E | (x - E) % 26 = t | t |
a | T | (a - T) % 26 = h | h |
i | V | (i - V) % 26 = e | e |
m | I | (m - I) % 26 = r | r |
m | C | (m - C) % 26 = e | e |
k | T | (k - T) % 26 = i | i |
l | O | (l - O) % 26 = s | s |
o | R | (o - R) % 26 = a | a |
n | Y | (n - Y) % 26 = w | w |
g | C (repeat) | (g - C) % 26 = i | i |
n | O (repeat) | (n - O) % 26 = l | l |
z | M (repeat) | (z - M) % 26 = l | l |
f | P (repeat) | (f - P) % 26 = t | t |
w | L (repeat) | (w - L) % 26 = h | h |
p | E (repeat) | (p - E) % 26 = e | e |
v | T (repeat) | (v - T) % 26 = r | r |
x | V (repeat) | (x - V) % 26 = e | e |
m | I (repeat) | (m - I) % 26 = i | i |
n | C (repeat) | (n - C) % 26 = s | s |
i | T (repeat) | (i - T) % 26 = a | a |
y | O (repeat) | (y - O) % 26 = w | w |
t | R (repeat) | (t - R) % 26 = a | a |
m | Y (repeat) | (m - Y) % 26 = y | y |
解密操作中的减法运算是基于英文字母表顺序从A
到Z
(或a
到z
)的映射到0-25的数字进行的,其中必要时会加上26以保持结果为正数。例如,(Y - C) % 26
实际上是 (24 - 2) % 26 = 22
,转换回字母就是W
。
最终,得到的明文是:Wherethereisawillthereisaway
。
【解题思路】
-
预处理密钥:
- 首先,将密钥
k
转换为大写字母。这一步简化了后续的解密过程,因为Vigenère密码的®运算忽略了大小写。通过将密钥统一为大写,我们可以统一处理密文中的大写和小写字母。
- 首先,将密钥
-
解密过程:
- 遍历密文的每个字符。对于密文中的每个字符,根据它是大写还是小写字母采取不同的操作,同时使用转换后的密钥字符进行解密。
- 如果当前字符是大写字母,将其与密钥中对应的字符(已转换为大写)进行®逆运算,得到明文中的对应大写字母。
- 如果当前字符是小写字母,采取相同的®逆运算,得到明文中的对应小写字母。
- 解密时,考虑字符的ASCII值进行运算,并利用模26来处理字母表的循环。
-
循环使用密钥:
- 由于密文的长度可能超过密钥的长度,因此在处理过程中需要循环使用密钥字符。这通过在每次使用密钥字符后更新密钥索引,并在索引等于密钥长度时重置索引来实现。
-
输出明文:
- 解密过程直接在输入的密文字符数组上操作,将每个密文字符转换为对应的明文字符。这样做避免了额外的字符串拼接或数组复制操作,提高了效率。
- 完成所有字符的解密后,输出转换后的密文数组,此时它已经是解密后的明文。
【代码实现】
#include <iostream>
#include <cstring> // 用于处理字符串,比如使用strlen()
using namespace std;
// 函数定义:转换字符为大写
void convertToUpper(char &ch) {
if(ch >= 'a' && ch <= 'z') {
ch -= 32; // 将小写字母转换为大写
}
}
int main() {
char key[100]; // 存储密钥
char cipherText[1000]; // 存储密文
cin >> key;
cin >> cipherText;
int keyLength = strlen(key); // 密钥的长度
int cipherLength = strlen(cipherText); // 密文的长度
// 将密钥转换为全大写
for(int i = 0; i < keyLength; i++) {
convertToUpper(key[i]);
}
int keyIndex = 0; // 用于遍历密钥的索引
// 遍历密文并进行解密
for(int i = 0; i < cipherLength; i++) {
// 密钥字符与'A'的距离,用于解密
int shift = key[keyIndex] - 'A';
if(cipherText[i] <= 'Z') { // 如果是大写字母
cipherText[i] -= shift;
if(cipherText[i] < 'A') { // 调整超出范围的字符
cipherText[i] += 26;
}
} else { // 如果是小写字母
cipherText[i] -= shift;
if(cipherText[i] < 'a') { // 调整超出范围的字符
cipherText[i] += 26;
}
}
keyIndex = (keyIndex + 1) % keyLength; // 更新密钥索引,循环使用密钥
}
cout << cipherText << endl; // 输出解密后的明文
return 0;
}