【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)”。
计算偏移量(找规律): 在 C++ 中,字符(
char)底层是以整数形式(ASCII 码)存储的,可以直接拿来做减法。 用密文的第一个字母减去明文的第一个字母,就能得到偏移量:int offset = ciphertext1[0] - plaintext1[0];注意:有些测试数据可能导致这个算出来的偏移量是个负数(例如从 Z 偏移回 A 这种倒退情况)。为了确保偏移量在 $0 \dots 25$ 这个正常的“正向轮回”区间里,我们可以用一种经典的小技巧:先把算出来的数加上 $26$,然后再对 $26$ 取模运算:offset = (offset + 26) % 26;把未知密文还原回明文(解密): 拿到了偏移量后,我们就可以去处理真正的未破解密文了。遍历这个字符串里的每一个字符,把密文字符往回退缩
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'
- 先把大写字母转成 $0 \dots 25$ 的相对序号:
示例代码
标准解法
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;
}
代码解析小贴士
% 26取模运算的妙用:在处理有关“星期几轮回”、“时钟环形指针表盘”或“英文字母成环轮回”这类问题时,几乎永远离不开取余数运算(%)。通过对首字母基准码的相减运算并结合% 26取余法则,能极其优雅地处理各种顺口溜般的跨界后滚翻问题。这是 GESP 三级和四级非常高频应用的一种小模板,值得死记硬背。- 安全防护(+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),考试认证学员交流,互帮互助
