最少字符串操作次数 | 豆包MarsCode AI 刷题

100 阅读3分钟

最少字符串操作次数 | 豆包MarsCode AI 刷题

问题描述

小U得到一个只包含小写字母的字符串 S。她可以执行如下操作:每次选择字符串中两个相同的字符删除,然后在字符串末尾添加一个任意的小写字母。小U想知道,最少需要多少次操作才能使得字符串中的所有字母都不相同

思路

一开始的思路是: 对于每个出现次数大于 1 的字符,每次可以消去两个相同字符并添加一个新字符,因此需要的操作次数为该字符出现次数的整除 2 的值(即 count//2count//2),将所有出现次数大于 1 的字符需要的操作次数累加即可得到最终结果。

代码:

def solution(S: str) -> int:
    # 创建一个字典来统计每个字符的出现次数
    dic = {}
    for c in S:
        # 如果字符已经存在于字典中,增加其计数;否则初始化为1
        dic[c] = dic.get(c, 0) + 1  
    # 对于每个频率v > 1的字符,计算v // 2并累加
    return sum(v // 2 for v in dic.values() if v > 1)  

上面的代码虽然可以AC,但并不完全正确,考虑下面的测试用例:

print(solution(S = "abcdefghijklmnopqrstuvwxyzaa") == 2) #False

代码返回的是 1 ,但实际上应该是 2 ,先去掉两个 a ,再添加一个 a ,然后再去掉两个 a 。其实是贪心策略有问题,贪心的最优策略应该是每次去找出现次数最高的字符 c ,把 c 的计数 -2 ,再去找出现次数最低的字符 c' ,把 c' 的计数 +1

  • 为什么选择频次最高的字符?

    • 字符 c 的频次最高,意味着它对整体的重复程度影响最大。
    • 如果不优先减少 c 的数量,其他策略会导致更多重复字符,增加后续操作次数。
  • 为什么在移除两个字符后添加频次最低的字符?

    • 添加频次最低的字符 d 是为了尽量避免引入新的重复。例如:

      • 如果当前字符频次为 0 ,则添加它不会导致重复;
      • 如果当前字符频次非 0 ,则添加它也只会造成最少的重复量增加。
代码实现
import string

def solution(S: str) -> int:
    ans = 0
    #初始化字母表的所有计数为0
    dic = {char: 0 for char in string.ascii_lowercase}
    for c in S:
        dic[c] += 1
    #所有字符的出现次数全部小于等于1时即为答案
    while not all(value <= 1 for value in dic.values()):
        #出现次数最高的字符计数-2
        max_key = max(dic, key=dic.get)
        dic[max_key] -= 2
        #出现次数最低的字符计数-1
        min_key = min(dic, key=dic.get)
        dic[min_key] += 1
        ans += 1
    return ans

if __name__ == '__main__':
    #test cases...
复杂度分析
  1. 时间复杂度

    • 统计字符频次的时间复杂度为 O(n)O(n),其中 nn 是字符串的长度。
    • 每次查找频次最高和最低的字符在最坏情况下需要遍历字典 O(26)O(26),即常数时间。
    • 最多进行 O(n) 次删除操作,因此总的时间复杂度为 O(n)O(n)
  2. 空间复杂度

    • 需要一个大小为 26 的字典存储每个字母的频次,空间复杂度为 O(1)O(1)