文章

【GESP】C++三级真题 luogu-B4500, [GESP202603 三级] 凯撒密码

2026年3月,GESP三级真题,考察字符串处理与 ASCII 字符偏移运算,难度★★☆☆☆。洛谷难度等级:入门

B4500 [GESP202603 三级] 凯撒密码

题目要求

题目描述

凯撒密码是一种替换加密技术,明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。例如,当偏移量是 3 的时候,所有的字母 A 将被替换成 D,B 被替换成 E,C 被替换成 F,以此类推,W 被替换成 Z,X 被替换成 A,Y 被替换成 B,Z 被替换成 C。这个加密方法是以罗马共和时期凯撒的名字命名的,据称当年凯撒曾用此方法与其将军们进行联系。

但是和所有的利用字母表进行替换的加密技术一样,凯撒密码非常容易被破解,而且在实际应用中也无法保证通信安全。

现在给你一个已破解的凯撒密码明文与密文,与一个有相同偏移量的未破解凯撒密码密文,请你帮忙破解它。

输入格式

输入共三行:

  • 第一行包含一个字符串,表示已破解的凯撒密码明文;
  • 第二行包含一个字符串,表示已破解的凯撒密码密文;
  • 第三行包含一个字符串,表示待破解的凯撒密码密文。

输出格式

输出一行,包含一个字符串,表示待破解的凯撒密码对应的明文。

输入输出样例 #1

输入 #1

1
2
3
ABCDEFGVWXYZ
DEFGHIJYZABC
WKHTXLFNEURZQIRAMXPSVRYHUWKHODCBGRJ

输出 #1

1
THEQUICKBROWNFOXJUMPSOVERTHELAZYDOG

说明/提示

样例解释

样例 1 中,通过已破解的密码得出偏移量为 'D' - 'A' = 3,因此,对未破解部分进行逆向偏移:密文中的 W 对应明文中的 T('W' - 3 = 'T'),密文中的 K 对应明文中的 H('K' - 3 = 'H'),以此类推。

数据范围

保证密码长度均不超过 1000,所有字符串由大写字母组成。


题目分析

这道题考察了 GESP 三级中的两个核心重点:字符串(string)的处理字符的 ASCII 码数学运算

题目给我们提供了一组已经破解好的“明文”与“密文”。因为凯撒密码的所有字母偏移量都是相同的,所以我们只需要拿出已破解的第一对字母(比如已知大写字母 A 变成了 D),去计算一下它们在字母表里的距离,就能反推出这个密码的“偏移量(offset)”。

  1. 计算偏移量(找规律): 在 C++ 中,字符(char)底层是以整数形式(ASCII 码)存储的,可以直接拿来做减法。 用密文的第一个字母减去明文的第一个字母,就能得到偏移量: int offset = ciphertext1[0] - plaintext1[0]; 注意:有些测试数据可能导致这个算出来的偏移量是个负数(例如从 Z 偏移回 A 这种倒退情况)。为了确保偏移量在 $0 \dots 25$ 这个正常的“正向轮回”区间里,我们可以用一种经典的小技巧:先把算出来的数加上 $26$,然后再对 $26$ 取模运算: offset = (offset + 26) % 26;

  2. 把未知密文还原回明文(解密): 拿到了偏移量后,我们就可以去处理真正的未破解密文了。遍历这个字符串里的每一个字符,把密文字符往回退缩 offset 步。 这里需要注意的是循环边界问题。假设密文字符是 'A',要往回退 3 步,按正常字母表顺序它应该退回到 'X'。如果在 C++ 里直接用 'A' - 3,会得到比 'A' 更小的非大写字母字符。 为了保证字母总能在 'A''Z' 的 26 个位置上进行正常的“贪吃蛇首尾循环”,标准的数学处理手法如下:

    • 先把大写字母转成 $0 \dots 25$ 的相对序号:ch - 'A'
    • 减去偏移量,加上 26 对整个结果取余:(ch - 'A' - offset + 26) % 26
    • 再把算出来的正确的 $0 \dots 25$ 的序号变回大写字母:... + 'A'


示例代码

标准解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <string>

int main() {
    // 定义三个字符串,分别存放已知明文、已知密文、待破解的密文
    std::string plaintext1, ciphertext1, ciphertext2;
    std::cin >> plaintext1 >> ciphertext1 >> ciphertext2;

    // 1. 求出加密用的标准偏移量 offset
    // 我们只需要用第一个已知密文字符减去第一个已知明文字符
    // 为了防止出现负数偏移或者越界,这里加上 26 再对 26 取余,确保它是一个标准的正向偏移。
    int offset = (ciphertext1[0] - plaintext1[0] + 26) % 26;

    // 初始化一个用来装配最终解密结果的字符串
    std::string result = "";

    // 2. 遍历待破解字符中的每一个密文字符
    for (int i = 0; i < ciphertext2.length(); i++) {
        char ch = ciphertext2[i];

        // 先算出该字符距离字母 'A' 有几个位置(相对序号 0-25)
        // 减去偏移量 offset,如果不够减出现了负数就先加上 26,再对 26 取模以实现轮回闭环。
        int decryptedIndex = (ch - 'A' - offset + 26) % 26;

        // 再把算好的新相对序号变回去,加上 'A' 的基数,变回大写字母
        char decryptedChar = 'A' + decryptedIndex;

        // 拼接到最终结果字符串末尾
        result += decryptedChar;
    }

    // 3. 打印最终解密好的明文
    std::cout << result << std::endl;

    return 0;
}

代码解析小贴士

  1. % 26 取模运算的妙用:在处理有关“星期几轮回”、“时钟环形指针表盘”或“英文字母成环轮回”这类问题时,几乎永远离不开取余数运算(%)。通过对首字母基准码的相减运算并结合 % 26 取余法则,能极其优雅地处理各种顺口溜般的跨界后滚翻问题。这是 GESP 三级和四级非常高频应用的一种小模板,值得死记硬背。
  2. 安全防护(+26):很多同学在 C++ 取模运算中会栽在负数上。在 C++ 里,-5 % 26 的结果是 -5,而不是我们现实数学日历期望的 21。所以要在可能产生负数的式子后面强行先填上一个轮回周期(即 + 26),把它变成正数后再取模,这是编程竞赛界最为经典的一个防负数越界小套路!

所有代码已上传至Github:https://github.com/lihongzheshuai/yummy-code

GESP 学习专题站:GESP WIKI

luogu-”系列题目可在洛谷题库进行在线评测。

bcqm-”系列题目可在编程启蒙题库进行在线评测。

欢迎加入Java、C++、Python技术交流QQ群(982860385),大佬免费带队,有问必答

欢迎加入C++ GESP/CSP认证学习QQ频道,考试资源总结汇总

欢迎加入C++ GESP/CSP学习交流QQ群(688906745),考试认证学员交流,互帮互助

GESP/CSP 认证学习微信公众号
GESP/CSP 认证学习微信公众号
本文由作者按照 CC BY-NC-SA 4.0 进行授权