题解:洛谷 B4556 字符转换
【题目来源】
洛谷:B4556 [GESP202606 三级] 字符转换 - 洛谷
【题目描述】
小杨同学有一串字符,里面可能有:
- 大写字母,比如 \(A\)、\(B\)、\(C\)
- 小写字母,比如 \(a\)、\(b\)、\(c\)
- 数字,比如 \(0\)、\(1\)、\(2\)
现在小杨同学想把这串字符变一变,规则如下:
- 如果是大写字母,就变成对应的小写字母;
- 如果是小写字母,就变成对应的大写字母;
- 如果是数字,就变成
*。
请你按照这个规则,帮小杨把整串字符转换好。
【输入】
输入一共有 \(2\) 行。
第一行:一个整数,表示这串字符一共有多少个字符。
第二行:一串连续的字符,中间没有空格。
【输出】
输出转换后的字符。
注意:输出时字符之间不要加空格。
【输入样例】
5
aBc98
【输出样例】
AbC**
【核心思想】
-
问题分析:给定长度为 \(n\) 的字符串 \(S\),需要对每个字符进行分类转换:大写字母 \(\to\) 小写字母、小写字母 \(\to\) 大写字母、数字 \(\to\)
*。本质是字符级别的条件映射问题,根据字符的 ASCII 属性执行不同的转换规则。 -
算法选择:
- 逐字符分类转换:遍历字符串,利用字符分类函数
islower()、isupper()、isdigit()判断类型,调用对应的大小写转换函数或替换为* - 原地修改:直接在原字符串上修改,无需额外空间
- 逐字符分类转换:遍历字符串,利用字符分类函数
-
关键步骤:
- 读取输入:\(n\)(字符串长度)和 \(S\)
- 遍历转换:对 \(S\) 的每个字符 \(S_i\):
- 若
islower(S_i)为真:\(S_i \leftarrow\)toupper(S_i)(小写转大写) - 否则若
isupper(S_i)为真:\(S_i \leftarrow\)tolower(S_i)(大写转小写) - 否则:\(S_i \leftarrow\)
*(数字等字符替换)
- 若
- 输出结果:输出转换后的字符串 \(S\)
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),遍历字符串一次,每个字符的分类和转换均为 \(O(1)\)
- 空间复杂度:\(O(1)\)(除输入字符串外),原地修改无需额外辅助空间
-
字符分类转换的核心思想:
- 类型三分支:将字符集划分为互斥的三个子集(小写字母、大写字母、其他),每个子集对应唯一的转换规则,通过
if-else if-else链实现清晰的分支逻辑 - ASCII 码运算:
toupper和tolower内部基于 ASCII 码的固定偏移(小写与大写相差 \(32\))实现 \(O(1)\) 转换,也可手动通过S[i] ^= 32(仅对字母)实现 - 原地覆盖策略:直接在原字符串上修改,避免创建新字符串带来的额外空间开销,符合"转换"而非"生成"的语义
- 防御性编程:使用
islower/isupper而非直接比较 ASCII 范围,代码可读性更强且能正确处理扩展字符集(尽管本题限定为字母和数字) - 适用于字符级别的条件替换、大小写互换、符号过滤等基础字符串处理问题
- 类型三分支:将字符集划分为互斥的三个子集(小写字母、大写字母、其他),每个子集对应唯一的转换规则,通过
【算法标签】
入门 #字符串入门
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n; // n: 字符串长度
string s; // s: 需要转换的字符串
int main()
{
cin >> n >> s; // 读入字符串长度和字符串本身
for (int i = 0; i < s.size(); i++) // 遍历字符串中的每个字符
{
if (islower(s[i])) // 如果是小写字母
s[i] = toupper(s[i]); // 转换为对应的大写字母
else if (isupper(s[i])) // 如果是大写字母
s[i] = tolower(s[i]); // 转换为对应的小写字母
else // 如果是数字等其他字符
s[i] = '*'; // 替换为 '*'
}
cout << s << endl; // 输出转换后的字符串
return 0;
}
【运行结果】
5
aBc98
AbC**
浙公网安备 33010602011771号