🔍 2025蓝桥杯备赛Day6——B2116 加密的病历单
🚀 题目速览
题目难度:⭐️⭐️ 适合掌握字符串操作与逆向思维
考察重点:大小写转换、字符串逆序、循环位移
B2116 加密的病历单
题目描述
小英是药学专业大三的学生,暑假期间获得了去医院药房实习的机会。
在药房实习期间,小英扎实的专业基础获得了医生的一致好评,得知小英在计算概论中取得过好成绩后,主任又额外交给她一项任务,解密抗战时期被加密过的一些伤员的名单。
经过研究,小英发现了如下加密规律(括号中是一个“原文 → 密文”的例子)
原文中所有的字符都在字母表中被循环左移了三个位置( bcd → yza \text{bcd} \to \text{yza} bcd→yza)
逆序存储( abcd → dcba \text{abcd} \to \text{dcba} abcd→dcba)
大小写反转( abXY → ABxy \text{abXY} \to \text{ABxy} abXY→ABxy)
现在给出一个加密的字符串,请你将其解密。
输入格式
一个加密的字符串。(长度小于 50 50 50 且只包含大小写字母)
输出格式
输出解密后的字符串。
输入输出样例 #1
输入 #1
GSOOWFASOq
输出 #1
Trvdizrrvj
🔥 解法一:分步处理法(推荐)
🛠️ 实现思路
逆向操作步骤:先处理最后一步加密操作,按加密规则的逆序逐步还原 算法优势:逻辑清晰,便于调试
#include <iostream>
#include <algorithm> // reverse函数需要
#include <cctype> // 大小写转换函数需要
using namespace std;
int main() {
string s;
cin >> s; // 读取加密字符串
// 第一步:恢复大小写(逆向第三加密步骤)
for (char &c : s) {
if (isupper(c)) c = tolower(c); // 大写转小写
else c = toupper(c); // 小写转大写
}
// 第二步:恢复顺序(逆向第二加密步骤)
reverse(s.begin(), s.end()); // 反转字符串
// 第三步:恢复位移(逆向第一加密步骤)
for (char &c : s) {
if (isupper(c)) {
// 大写字母循环右移3位:(原字符 - 'A' + 3) % 26 + 'A'
c = (c - 'A' + 3) % 26 + 'A';
} else {
// 小写字母循环右移3位:(原字符 - 'a' + 3) % 26 + 'a'
c = (c - 'a' + 3) % 26 + 'a';
}
}
cout << s; // 输出解密结果
return 0;
}
🔥 解法二:函数式编程(工程级实现)
🛠️ 实现思路
模块化设计:将各步骤拆分为独立函数,提高代码复用性
工程优势:便于单元测试和功能扩展
#include <iostream>
#include <algorithm>
#include <cctype>
using namespace std;
// 反转字符串大小写
void invertCase(string &s) {
for (char &c : s) {
c = isupper(c) ? tolower(c) : toupper(c); // 三元运算符简化判断
}
}
// 循环右移三个字符
void shiftRight(string &s) {
for (char &c : s) {
int base = isupper(c) ? 'A' : 'a'; // 根据大小写确定基准
c = (c - base + 3) % 26 + base; // 右移3位并保证循环
}
}
int main() {
string s;
cin >> s;
invertCase(s); // 逆向第三步加密
reverse(s.begin(), s.end()); // 逆向第二步加密
shiftRight(s); // 逆向第一步加密
cout << s;
return 0;
}
📚 知识点总结
一、核心函数详解
1. reverse()
函数
功能:反转容器(如字符串、数组)中元素的顺序
头文件:
#include <algorithm>
语法:reverse(起始迭代器, 结束迭代器)
reverse(s.begin(), s.end()); // 反转整个字符串
时间复杂度:O(n)(每个元素交换一次)
边界处理:若范围为空(
begin == end
),函数不执行任何操作
2. isupper()
函数
功能:检测字符是否为大写字母(A-Z)
头文件:
#include <cctype>
语法:int isupper(int c)
char c = 'A'; if (isupper(c)) { // 条件为真 // 处理大写字母 }
返回值:非零值(通常为1)表示是大写字母,0表示不是
3. tolower()
函数
功能:将大写字母转换为小写字母
头文件:
#include <cctype>
语法:int tolower(int c)
char c = 'B'; char lower_c = tolower(c); // lower_c = 'b'
注意事项:非大写字母输入时返回原字符
二、循环位移算法解析
数学公式
大写字母右移3位:
偏移值 = (原字符 - 'A' + 3) % 26 + 'A'
- 模26:确保循环(如’X’ → ‘A’)
- 示例:‘X’(ASCII 88) → (88-65+3)%26=26%26=0 → 0+65=65 → ‘A’
小写字母右移3位:
偏移值 = (原字符 - 'a' + 3) % 26 + 'a'
- 示例:‘x’(ASCII 120) → (120-97+3)%26=26%26=0 → 0+97=97 → ‘a’
🚨 常见错误警示
错误1:操作顺序错误
// 错误:先位移后反转
shiftRight(s);
reverse(s.begin(), s.end()); // 导致结果错误
修正:严格按照加密的逆序操作(先反转大小写,再逆序,最后位移)
错误2:未处理字符溢出
// 错误:直接加3未取模
c = c + 3; // 'x'→ASCII 120+3=123→'{'(非字母)
修正:必须使用模26运算保证循环:
c = (c - 'a' + 3) % 26 + 'a'; // 'x'→'a'
错误3:忽略大小写判断
// 错误:未区分大小写直接计算
c = (c - 'A' + 3) % 26 + 'A'; // 若c是小写字母,结果错误
修正:需先判断大小写:
if (isupper(c)) {
// 处理大写
} else {
// 处理小写
}
📊 性能对比分析
解法 | 时间复杂度 | 空间复杂度 | 适用场景 |
---|---|---|---|
分步处理法 | O(n) | O(1) | 快速实现、调试简单 |
函数式编程 | O(n) | O(1) | 工程化开发、可扩展 |
暴力解法 | O(n) | O(n) | 仅用于理论验证 |
🌟 举一反三
变种题1:自定义加密规则
假设加密规则为:
- 循环左移5位
- 大小写反转
- 逆序存储
解密步骤:
- 逆序(恢复步骤3)
- 反转大小写(恢复步骤2)
- 循环右移5位(恢复步骤1)
变种题2:多层加密
若加密步骤重复多次(如两次循环左移),需分析加密次数与解密次数的关系:
// 加密:循环左移3位 × 2次 → 等效左移6位
// 解密:循环右移6位 或 右移(3×2) % 26位
🛠️ 实战技巧
1. 调试技巧
分步打印中间结果:
cout << "After case invert: " << s << endl;
reverse(s.begin(), s.end());
cout << "After reverse: " << s << endl;
shiftRight(s);
cout << "After shift: " << s << endl;
2. 性能优化
预分配内存:
s.reserve(50); // 根据题目最大长度预分配
3. 边界测试用例
输入 | 预期输出 | 测试目的 |
---|---|---|
XYZ (全大写) |
ABC |
大写字母循环右移 |
abc (全小写) |
xyz |
小写字母循环右移 |
AbCd (混合) |
XyZa |
混合大小写处理 |
蓝桥杯考场策略:
- 优先选择解法一:代码紧凑,易于调试
- 严格步骤顺序:先反转大小写 → 逆序 → 右移
- 模块化训练:将常用操作(如循环位移)封装为函数
👉 思考题:若加密新增数字处理(如’0’→’9’循环右移3位),如何扩展代码? 答案提示:在位移步骤中添加数字分支:
if (isdigit(c)) {
c = (c - '0' + 3) % 10 + '0'; // 数字循环右移
}