最少字符串操作次数问题总结
一、题目描述与理解
题目要求我们处理一个只包含小写字母的字符串S,可以执行如下操作:每次可以选择字符串中两个相同的字符删除,然后在字符串末尾添加一个任意的小写字母。目标是计算最少需要多少次操作才能使字符串中的所有字母都不相同。
示例:
- 输入:"abab" 输出:2
- 输入:"aaaa" 输出:2
- 输入:"abcabc" 输出:3
二、解题思路分析
1. 核心思路
这道题的关键在于理解:我们需要通过删除相同字符并添加新字符的方式,最终达到所有字符都不相同的状态。主要思路如下:
- 统计每个字符的出现频次
- 对于出现次数大于1的字符,需要通过操作将其降至1次
- 每次操作会减少两个相同字符,增加一个新字符
- 需要贪心地处理出现次数最多的字符
2. 算法步骤
- 使用字典统计每个字符出现的次数
- 将字符出现次数转换为列表并降序排序
- 循环处理直到所有字符出现次数均为1:
- 选择出现次数最多的字符
- 减少其出现次数2次
- 添加一个新字符(计数为1)
- 操作次数加1
- 返回总操作次数
三、代码实现与详解
def solution(S: str) -> int:
# 统计字符频次
char_count = {}
for char in S:
char_count[char] = char_count.get(char, 0) + 1
# 将频次排序
counts = sorted(char_count.values(), reverse=True)
operations = 0
while True:
# 检查终止条件
if all(count <= 1 for count in counts):
break
# 处理出现次数最多的字符
for i in range(len(counts)):
if counts[i] > 1:
counts[i] -= 2 # 删除两个相同字符
counts.append(1) # 添加新字符
operations += 1
break
# 重新排序保证处理最高频字符
counts.sort(reverse=True)
return operations
四、复杂度分析
-
时间复杂度:O(n*log n)
- 字符统计:O(n)
- 排序:O(k*log k),k为不同字符数量
- 循环处理:最坏情况O(n*log n)
-
空间复杂度:O(k)
- 存储字符频次:O(k)
- 排序数组:O(k)
五、常见错误分析
- 贪心策略错误 最初可能会想到简单地每次处理两个相同字符,但这样可能导致操作次数不是最少。正确的方式是优先处理出现次数最多的字符。
错误示例:
def wrong_solution(S: str) -> int:
chars = list(S)
operations = 0
while True:
found = False
for i in range(len(chars)-1):
for j in range(i+1, len(chars)):
if chars[i] == chars[j]:
chars.pop(j)
chars.pop(i)
chars.append('a')
operations += 1
found = True
break
if found:
break
if not found:
break
return operations
- 终止条件判断错误 没有正确判断所有字符都不相同的情况,可能导致死循环或提前终止。
六、优化思考
-
数据结构优化 可以考虑使用堆(优先队列)来维护字符频次,这样可以避免重复排序,优化时间复杂度。
-
贪心策略优化 可以证明,每次处理出现次数最多的字符是最优的,因为:
- 最终状态要求所有字符出现次数不超过1
- 每次操作只能减少2个字符
- 处理高频字符可以最快地达到目标状态
七、总结与思考
这道题是一个典型的贪心算法问题,关键在于:
- 正确理解题目要求和操作规则
- 找到贪心策略的正确性证明
- 高效实现算法逻辑
通过这道题,我们可以学到:
- 字符串处理的基本技巧
- 贪心算法的应用
- 如何优化算法的时间和空间复杂度