使字符出现次数唯一的最小删除数| 豆包MarsCode AI 刷题

122 阅读6分钟

刷题笔记:使字符出现次数唯一的最小删除数

问题描述

给定一个字符串 s,你需要通过删除一些字符,使得每个字符在字符串中出现的次数均不相同。目标是找出最少需要删除的字符数量以达到这个目标。

示例

  • 示例1

    • 输入: s = "aab"
    • 输出: 0
    • 解释: 字符 'a' 出现2次,字符 'b' 出现1次,这些出现次数已经不同,无需删除。
  • 示例2

    • 输入: s = "aaabbbcc"
    • 输出: 2
    • 解释: 可以删除两个 'b',使得 'a' 出现3次,'b' 出现1次,'c' 出现2次,频率均不同。
  • 示例3

    • 输入: s = "abcdef"
    • 输出: 5
    • 解释: 所有字符出现次数均为1,只有一个字符可以保留,其他5个字符需要删除。

解题思路

为了确保每个字符的出现次数都是唯一的,我们需要:

  1. 统计字符频率:首先统计每个字符在字符串中出现的次数。

  2. 确保频率唯一

    • 使用一个集合来记录已经使用过的频率。
    • 遍历每个字符的频率,如果当前频率已经被使用过,则减少该频率(相当于删除一个字符),直到找到一个未被使用的频率或频率降为0。
    • 记录每次减少频率的操作次数,这些操作次数就是需要删除的字符数量。
  3. 返回删除总数:所有操作完成后,返回删除的字符总数。

执行过程示例

让我们通过示例2 "aaabbbcc" 来演示代码的执行过程。

示例输入

s = "aaabbbcc"

执行步骤详解

  1. 统计字符频率

    freq := make(map[rune]int)
    for _, char := range s {
        freq[char]++
    }
    

    结果

    freq = {
        'a': 3,
        'b': 3,
        'c': 2
    }
    
  2. 初始化辅助数据结构

    usedFreqs := make(map[int]bool) // 存储已使用的频率
    deletions := 0                    // 删除的字符总数
    
  3. 遍历字符频率并确保唯一性

    for _, count := range freq {
        for count > 0 && usedFreqs[count] {
            count--
            deletions++
        }
        if count > 0 {
            usedFreqs[count] = true
        }
    }
    

    详细过程

    • 处理字符 'a',频率 3

      • 检查 3 是否已在 usedFreqs 中:
        • usedFreqs[3]false(尚未使用)
      • 3 添加到 usedFreqs
        usedFreqs = {3: true}
        
      • 删除次数 deletions0
    • 处理字符 'b',频率 3

      • 检查 3 是否已在 usedFreqs 中:
        • usedFreqs[3]true(已使用)
      • 将频率减少到 2,并增加删除计数:
        count = 2
        deletions = 1
        
      • 检查 2 是否已在 usedFreqs 中:
        • usedFreqs[2]false(尚未使用)
      • 2 添加到 usedFreqs
        usedFreqs = {3: true, 2: true}
        
      • 删除次数 deletions1
    • 处理字符 'c',频率 2

      • 检查 2 是否已在 usedFreqs 中:
        • usedFreqs[2]true(已使用)
      • 将频率减少到 1,并增加删除计数:
        count = 1
        deletions = 2
        
      • 检查 1 是否已在 usedFreqs 中:
        • usedFreqs[1]false(尚未使用)
      • 1 添加到 usedFreqs
        usedFreqs = {3: true, 2: true, 1: true}
        
      • 删除次数 deletions2
  4. 最终结果

    return deletions // 返回 2
    

    删除总数2

Python 实现

from collections import Counter

def solution(s: str) -> int:
    # 统计每个字符的出现频率
    freq = Counter(s)
    
    used_freqs = set()  # 用于存储已使用的频率
    deletions = 0       # 删除的字符总数
    
    for char, count in freq.items():
        # 当当前频率已经被使用,且大于0时,减少频率并增加删除计数
        while count > 0 and count in used_freqs:
            count -= 1
            deletions += 1
        if count > 0:
            used_freqs.add(count)  # 将新的唯一频率加入已使用集合
    
    return deletions

if __name__ == '__main__':
    print(solution("aab") == 0)        # 预期输出: True
    print(solution("aaabbbcc") == 2)  # 预期输出: True
    print(solution("abcdef") == 5)     # 预期输出: True

代码解释

  1. 统计字符频率

    • 使用 Counter(s) 统计字符串中每个字符出现的次数,结果存储在 freq 中。
  2. 确保频率唯一

    • 初始化一个空集合 used_freqs 用于记录已使用的频率。
    • 遍历 freq 中每个字符及其频率:
      • 如果当前频率已经在 used_freqs 中存在,则减少该频率(相当于删除一个字符),并增加删除计数 deletions
      • 重复上述步骤,直到找到一个未被使用的频率或频率降为0。
      • 如果调整后的频率大于0,将其添加到 used_freqs 中。
  3. 返回结果

    • 最终返回 deletions,即为使所有字符频率唯一所需的最少删除次数。

Go 语言实现

package main

import (
	"fmt"
)

// solution 函数用于计算需要删除的最少字符数,使得每个字符的出现次数都唯一
func solution(s string) int {
	// 统计每个字符的出现频率
	freq := make(map[rune]int)
	for _, char := range s {
		freq[char]++
	}

	usedFreqs := make(map[int]bool) // 用于存储已使用的频率
	deletions := 0                    // 删除的字符总数

	for _, count := range freq {
		// 当当前频率已经被使用,且大于0时,减少频率并增加删除计数
		for count > 0 && usedFreqs[count] {
			count--
			deletions++
		}
		if count > 0 {
			usedFreqs[count] = true // 将新的唯一频率加入已使用集合
		}
	}

	return deletions
}

func main() {
	// 测试用例
	fmt.Println(solution("aab") == 0)        // 预期输出: true
	fmt.Println(solution("aaabbbcc") == 2)  // 预期输出: true
	fmt.Println(solution("abcdef") == 5)     // 预期输出: true
}

代码解释

  1. 统计字符频率

    • 使用 map[rune]int 来存储每个字符的出现次数。
    • 遍历字符串 s,统计每个字符的频率。
  2. 确保频率唯一

    • 使用 map[int]bool 来记录已经使用过的频率。
    • 遍历 freq 中每个字符的频率:
      • 如果当前频率已经在 usedFreqs 中存在,则减少该频率,并增加删除计数 deletions
      • 重复上述步骤,直到找到一个未被使用的频率或频率降为0。
      • 如果调整后的频率大于0,将其标记为已使用。
  3. 返回结果

    • 最终返回 deletions,即为使所有字符频率唯一所需的最少删除次数。

总结

通过上述步骤,我们成功地将字符串 "aaabbbcc" 转换为每个字符出现次数唯一的形式,所需的最少删除次数为 2

复杂度分析

  • 时间复杂度O(n),其中 n 是字符串的长度。

    • 统计字符频率需要遍历整个字符串,时间复杂度为 O(n)
    • 遍历字符频率并调整频率的过程,最坏情况下也需要 O(n) 的时间。
  • 空间复杂度O(k),其中 k 是字符串中不同字符的数量。

    • 需要额外的空间来存储字符频率和已使用的频率。

关键点总结

  1. 频率统计:使用高效的数据结构(如 Counter 在 Python 中,map 在 Go 中)来统计字符频率。

  2. 确保唯一性

    • 使用集合或映射来记录已使用的频率。
    • 通过减少频率来避免冲突,记录每次减少的操作次数。
  3. 优化思路

    • 处理频率冲突时,尽量减少删除次数,以达到最优解。
    • 可以从高频率开始处理,以减少整体删除次数。
  4. 实现语言的选择

    • Python 提供了便捷的 Counter 类,简化频率统计。
    • Go 语言则通过 map 来实现类似的功能,代码逻辑清晰。