信息学奥赛一本通题目解析:1869:【12NOIP提高组】Vigenère密码(字符串)

文章讲述了Vigenère密码的加密与解密方法,涉及大小写处理、ASCII映射及密钥循环使用,提供了C++代码示例。
摘要由CSDN通过智能技术生成

描述

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。

明文Helloworld
密钥abcabcabca
密文Hfnlpyosnd

输入

输入共2行。
第一行为一个字符串,表示密钥k,长度不超过100,其中仅包含大小写字母。第二行为一个字符串,表示经加密后的密文,长度不超过1000,其中仅包含大小写字母。

对于100%的数据,输入的密钥的长度不超过100,输入的密文的长度不超过1000,且都仅包含英文字母。

输出

输出共1行,一个字符串,表示输入密钥和密文所对应的明文。

样例输入

CompleteVictory
Yvqgpxaimmklongnzfwpvxmniytm

样例输出

Wherethereisawillthereisaway

【样例分析】

  • 密钥(key): CompleteVictory
  • 密文(cipherText): Yvqgpxaimmklongnzfwpvxmniytm

转换为明文的详细步骤如下:

首先,将密钥转换为全大写:COMPLETEVICTORY

接下来,我们逐个字符地解密密文。每个字符的解密过程如下:

密文字符密钥字符解密操作和结果明文字符
YC(Y - C) % 26 = WW
vO(v - O) % 26 = hh
qM(q - M) % 26 = ee
gP(g - P) % 26 = rr
pL(p - L) % 26 = ee
xE(x - E) % 26 = tt
aT(a - T) % 26 = hh
iV(i - V) % 26 = ee
mI(m - I) % 26 = rr
mC(m - C) % 26 = ee
kT(k - T) % 26 = ii
lO(l - O) % 26 = ss
oR(o - R) % 26 = aa
nY(n - Y) % 26 = ww
gC (repeat)(g - C) % 26 = ii
nO (repeat)(n - O) % 26 = ll
zM (repeat)(z - M) % 26 = ll
fP (repeat)(f - P) % 26 = tt
wL (repeat)(w - L) % 26 = hh
pE (repeat)(p - E) % 26 = ee
vT (repeat)(v - T) % 26 = rr
xV (repeat)(x - V) % 26 = ee
mI (repeat)(m - I) % 26 = ii
nC (repeat)(n - C) % 26 = ss
iT (repeat)(i - T) % 26 = aa
yO (repeat)(y - O) % 26 = ww
tR (repeat)(t - R) % 26 = aa
mY (repeat)(m - Y) % 26 = yy

解密操作中的减法运算是基于英文字母表顺序从AZ(或az)的映射到0-25的数字进行的,其中必要时会加上26以保持结果为正数。例如,(Y - C) % 26 实际上是 (24 - 2) % 26 = 22,转换回字母就是W

最终,得到的明文是:Wherethereisawillthereisaway

【解题思路】

  1. 预处理密钥

    • 首先,将密钥k转换为大写字母。这一步简化了后续的解密过程,因为Vigenère密码的®运算忽略了大小写。通过将密钥统一为大写,我们可以统一处理密文中的大写和小写字母。
  2. 解密过程

    • 遍历密文的每个字符。对于密文中的每个字符,根据它是大写还是小写字母采取不同的操作,同时使用转换后的密钥字符进行解密。
    • 如果当前字符是大写字母,将其与密钥中对应的字符(已转换为大写)进行®逆运算,得到明文中的对应大写字母。
    • 如果当前字符是小写字母,采取相同的®逆运算,得到明文中的对应小写字母。
    • 解密时,考虑字符的ASCII值进行运算,并利用模26来处理字母表的循环。
  3. 循环使用密钥

    • 由于密文的长度可能超过密钥的长度,因此在处理过程中需要循环使用密钥字符。这通过在每次使用密钥字符后更新密钥索引,并在索引等于密钥长度时重置索引来实现。
  4. 输出明文

    • 解密过程直接在输入的密文字符数组上操作,将每个密文字符转换为对应的明文字符。这样做避免了额外的字符串拼接或数组复制操作,提高了效率。
    • 完成所有字符的解密后,输出转换后的密文数组,此时它已经是解密后的明文。

【代码实现】

#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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值