这是一道经典的字符串问题,该问题要求我们将一个压缩后的字符串还原回原本的字符串。该问题的主要难度在于字符串的解析,下面就和我一起看下这个问题的解答过程吧。
问题描述
- 输入字符串是一种压缩形式:
- 每个字母后面可能跟一个数字,该数字表示该字母在解压后需要重复的次数。
- 如果一个字母后没有数字,则该字母在解压后只出现一次。
- 需要根据上述规则生成解压后的完整字符串。
分析规则
-
字母后面有数字:
- 数字表示该字母的重复次数。
- 如输入
"a2",解压结果为"aa"。 - 如输入
"b3",解压结果为"bbb"。
-
字母后面无数字:
- 默认情况下,字母只出现一次。
- 如输入
"c",解压结果为"c"。
-
多位数字支持:
- 如果一个字母后跟多位数字,如
"d12",需要将12解析为一个整体,表示字母'd'重复12次。
- 如果一个字母后跟多位数字,如
-
边界情况:
- 空字符串输入时,输出也为空。
- 不包含数字的字符串,如
"abc",输出为"abc"。
测试样例分析
示例 1:
输入:"a2b3c4"
解压过程:
'a'后面有2:重复 2 次 →"aa"'b'后面有3:重复 3 次 →"bbb"'c'后面有4:重复 4 次 →"cccc"
最终结果:"aabbbcccc"
示例 2:
输入:"d5ef2"
解压过程:
'd'后面有5:重复 5 次 →"ddddd"'e'没有数字:重复 1 次 →"e"'f'后面有2:重复 2 次 →"ff"
最终结果:"dddddeff"
示例 3:
输入:"x3y1z"
解压过程:
'x'后面有3:重复 3 次 →"xxx"'y'后面有1:重复 1 次 →"y"'z'没有数字:重复 1 次 →"z"
最终结果:"xxxyz"
解题思路
- 遍历字符串:
- 按顺序解析每个字符。
- 判断该字符后是否有数字。
- 解析数字:
- 如果有数字,则读取完整数字(支持多位)。
- 如果没有数字,默认为
1。
- 处理重复:
- 根据解析的数字,将当前字母重复相应次数,并追加到结果字符串中。
- 跳过已解析部分:
- 对于已经处理的字符和数字部分,直接跳过,继续解析下一个字符。
算法流程
- 初始化结果字符串
result和索引变量i。 - 使用循环逐字符解析:
- 当前字符为字母,将其保存。
- 检查其后是否有数字,解析完整数字(如果存在)。
- 按解析的数字将字母重复多次并追加到结果字符串中。
- 返回结果字符串。
边界条件
- 空输入:
- 输入为空字符串时,直接返回空字符串。
- 无数字:
- 如果所有字母后都没有数字,如
"abc",直接将字母拼接到结果。
- 如果所有字母后都没有数字,如
- 多位数字:
- 如果一个字母后有多位数字,如
"d123",需正确解析123并将'd'重复123次。
- 如果一个字母后有多位数字,如
- 特殊字符:
- 输入中不应包含非字母或数字的字符。如果包含,需要进一步处理异常。
代码实现
string solution(string s) {
string result; // 存储解压后的字符串
int i = 0; // 当前索引
while (i < s.length()) {
char ch = s[i]; // 当前字符
i++;
int num = 0;
int nonum = 1;
// 检查字符后是否跟数字,并解析数字
while (i < s.length() && isdigit(s[i])) {
nonum = 0;
num = num * 10 + (s[i] - '0');
i++;
}
// 如果没有数字,默认重复 1 次
if (nonum) {
num = 1;
}
// 如果数字是 0,跳过当前字符
if (num == 0 && !nonum) {
continue;
}
// 将字符重复 num 次并添加到结果
result.append(num, ch);
}
return result;
}
具体解析
1. 初始化变量
代码中初始化了以下变量:
result:用来存储最终解压后的字符串。i:索引变量,用于遍历输入字符串s。num:用于记录当前字母的重复次数。nonum:一个辅助标志,用于判断当前字母后是否有数字。
通过这些变量的协作,我们能够实现对字符串的逐步解析,并根据规则正确处理每个字符及其重复次数。
2. 遍历字符串
while (i < s.length()) 循环保证我们可以逐字符地处理字符串的每一部分:
char ch = s[i];:获取当前字符并将索引i前移。i++:向前移动索引以检查字符后是否存在数字。
3. 解析数字
在每次获取字符后,代码需要判断该字符后是否有数字:
- 使用
isdigit(s[i])判断当前索引位置的字符是否为数字。 - 如果是数字,将其转化为整数,更新变量
num。
这一部分关键在于多位数字的处理。如果数字由多位组成,例如 d123,代码会通过 num = num * 10 + (s[i] - '0'); 累加解析所有数字。每次解析到一个数字,索引 i 继续向前移动,直到遇到非数字字符或字符串末尾。
4. 判断是否存在数字
为了处理字符后没有数字的情况,设置了 nonum 变量:
- 初始值为
1,表示尚未遇到数字。 - 如果进入
while (isdigit(s[i]))循环,nonum被设置为0,表示当前字符后有数字。
在循环结束后,若 nonum 仍为 1,说明该字符后没有数字,按照规则设置 num = 1。
5. 跳过无效字符
特殊情况是字符后数字为 0 的处理:
- 如果
num == 0 && !nonum,说明数字明确为0,此时直接跳过该字符,不将其添加到结果中。
这一逻辑确保了解压缩的正确性:字符后数字为 0 时应忽略该字符。
6. 生成解压结果
对于每个有效字符,代码使用 result.append(num, ch); 将字符重复 num 次添加到 result 中。string::append(size_t, char) 是 C++ STL 提供的高效方法,能够快速将字符多次添加到字符串末尾。
7. 返回结果
遍历完整个字符串后,函数返回 result,即解压缩后的字符串。
正确性分析
- 多位数字解析
- 使用
num = num * 10 + (s[i] - '0')能正确解析多位数字,例如d123。 - 在循环中只处理连续数字部分,确保解析到的数字属于当前字符。
- 使用
- 无数字处理
- 辅助标志
nonum确保能够正确处理无数字的字符,默认重复 1 次。
- 辅助标志
- 忽略 0 的字符
- 当数字为
0时跳过该字符,符合题目要求,保证结果正确。
- 当数字为
- 边界条件
- 字符串为空时,
while循环直接跳过,返回空字符串,符合预期。 - 如果数字在字符串末尾,
isdigit判断能正确处理,无需额外校验。
- 字符串为空时,
- 时间复杂度
- 每个字符和数字只遍历一次,因此时间复杂度为 (O(n)),适合大部分场景。
总结
这段代码通过高效的字符与数字解析逻辑,能够正确处理无数字、多位数字和数字为 0 的情况。时间复杂度 (O(n)) 和空间复杂度 (O(n)) 使其适用于大规模输入。代码逻辑清晰,功能完备,适合解压缩类似格式的字符串。