python 字符串压缩(字符串-中等)含源码(九)
问题说明(含示例)
问题描述:实现字符串压缩功能,利用字符重复出现的次数,将连续重复的字符替换为 “字符 + 次数” 的形式。若压缩后的字符串长度未小于原字符串,则返回原字符串。字符串仅包含大小写英文字母(a-z, A-Z)。
示例:
输入:
"aabcccccaaa"
输出:"a2b1c5a3"
解释:连续字符分别为 "aa"(2 次)、"b"(1 次)、"ccccc"(5 次)、"aaa"(3 次),压缩后长度更短。输入:
"abbccd"
输出:"abbccd"
解释:压缩后为 "a1b2c2d1"(长度 7),比原字符串(长度 6)更长,故返回原字符串。输入:
"aaabccaaaaaddd"
输出:"a3b1c2a5d3"
解释:连续字符对应次数为 3、1、2、5、3,压缩后长度更短。输入:
"aabcaddd"
输出:"aabcaddd"
解释:压缩后为 "a2b1c1a1d3"(长度 10),比原字符串(长度 8)更长,故返回原字符串。
解题关键
核心思路是遍历字符串,统计连续重复字符的次数,拼接压缩结果后与原字符串比较长度,具体步骤:
特殊情况处理:若原字符串长度≤1,直接返回原字符串(无法压缩变短)。
初始化变量:
result
:用列表存储压缩后的字符和次数(列表拼接效率高于字符串);prev_char
:记录当前遍历的前一个字符(初始为字符串第一个字符);count
:记录当前字符连续出现的次数(初始为 1)。
遍历字符串(从第二个字符开始):
若当前字符与
prev_char
相同,count += 1
;若不同,将
prev_char
和count
加入result
,更新prev_char
为当前字符,重置count = 1
。
处理最后一组字符:遍历结束后,将最后一组
prev_char
和count
加入result
。比较长度:将
result
转换为字符串,若长度 < 原字符串长度,返回压缩结果;否则返回原字符串。
对应代码
class Solution:def compressString(self, S: str) -> str:# 特殊情况:空字符串或长度为1的字符串,直接返回原字符串if len(S) <= 1:return S# 初始化变量:用列表存储结果(效率高于字符串拼接)result = []prev_char = S[0] # 前一个字符,初始为第一个字符count = 1 # 连续出现的次数# 从第二个字符开始遍历for char in S[1:]:if char == prev_char:# 当前字符与前一个相同,次数+1count += 1else:# 不同字符:将前一个字符和次数加入结果,更新前一个字符和次数result.append(prev_char)result.append(str(count))prev_char = charcount = 1# 处理最后一组字符(循环结束后未加入的部分)result.append(prev_char)result.append(str(count))# 转换为字符串,比较长度compressed = ''.join(result)return compressed if len(compressed) < len(S) else S
对应的基础知识
实现该算法需掌握以下基础概念与操作:
字符串遍历与索引
通过
for char in S[1:]
遍历字符串从第二个字符开始的所有元素;用
S[0]
访问字符串第一个字符,len(S)
获取字符串长度。
列表的拼接与转换
用列表
result
存储中间结果,通过append()
方法添加字符和次数(字符串拼接+=
效率低,因为字符串是不可变对象,每次拼接都会创建新对象);最终通过
''.join(result)
将列表转换为字符串,效率高于多次字符串拼接。
条件判断与计数更新
核心逻辑
if char == prev_char: count += 1 else: ...
用于判断字符是否连续重复,更新计数或切换字符。
边界处理
初始判断
if len(S) <= 1
处理短字符串(无法压缩变短),避免无效计算;遍历结束后手动添加最后一组字符,避免遗漏(因循环仅在字符变化时添加,最后一组无后续字符触发添加)。
对应的进阶知识
该问题的解决涉及字符串优化与复杂度分析的进阶思想:
时间与空间复杂度
时间复杂度:
O(n)
(n
为字符串长度),仅遍历一次字符串,列表append
和join
操作均为线性时间;空间复杂度:
O(n)
,最坏情况下(所有字符均不重复),result
列表需存储2n
个元素(每个字符 + 次数 “1”)。
字符串拼接的效率优化
Python 中字符串是不可变对象,使用
str += new_char
进行拼接时,每次都会创建新字符串(复制原有内容 + 新增部分),时间复杂度为O(k)
(k
为当前字符串长度),总时间可能达到O(n²)
;用列表
append
存储中间结果,最后join
转换,总时间为O(n)
(append
为O(1)
amortized,join
为O(n)
),是大规模字符串处理的推荐方式。
压缩逻辑的完整性
计数为 1 时必须保留(如示例 2 中 “a” 压缩为 “a1”),否则会导致信息丢失(如 “ab” 压缩为 “ab” 与原字符串相同,但按规则需压缩为 “a1b1” 再比较长度);
遍历结束后需手动添加最后一组字符,是因为循环仅在 “字符变化” 时触发添加,最后一组字符无后续变化,需单独处理。
编程思维与启示
“效率优先” 的细节处理:选择列表而非字符串拼接,体现了对数据结构特性的理解 —— 针对频繁修改的场景,可变对象(列表)比不可变对象(字符串)更高效。
“完整性” 的边界意识:遍历结束后处理最后一组字符,避免了 “循环漏处理” 的常见错误,体现了 “收尾逻辑” 的重要性(类似数组遍历中对最后一个元素的单独判断)。
“条件反转” 的比较逻辑:最终返回
compressed if len(compressed) < len(S) else S
,通过一次比较即可决定结果,避免冗余判断(无需单独判断 “大于” 或 “等于”)。“问题转化” 的简化思路:将 “压缩字符串” 问题转化为 “统计连续字符次数 + 拼接 + 长度比较” 的子问题,通过拆解步骤降低复杂度,这种 “分而治之” 的思维适用于多数字符串处理问题。