字符频次唯一的最小删除次数

标签: 贪心 哈希表 字符串 排序

难度: Medium

如果字符串 s不存在 两个不同字符 频次 相同的情况,就称 s优质字符串

给你一个字符串 s,返回使 s 成为 优质字符串 需要删除的 最小 字符数。

字符串中字符的 频次 是该字符在字符串中的出现次数。例如,在字符串 "aab" 中,'a' 的频次是 2,而 'b' 的频次是 1

 

示例 1:

输入:s = "aab"
输出:0
解释:s 已经是优质字符串。

示例 2:

输入:s = "aaabbbcc"
输出:2
解释:可以删除两个 'b' , 得到优质字符串 "aaabcc" 。
另一种方式是删除一个 'b' 和一个 'c' ,得到优质字符串 "aaabbc" 。

示例 3:

输入:s = "ceabaacb"
输出:2
解释:可以删除两个 'c' 得到优质字符串 "eabaab" 。
注意,只需要关注结果字符串中仍然存在的字符。(即,频次为 0 的字符会忽略不计。)

 

提示:

  • 1 <= s.length <= 105
  • s 仅含小写英文字母

Submission

运行时间: 96 ms

内存: 16.2 MB

class Solution:
    def minDeletions(self, s: str) -> int:
        freq = Counter(s)
        #value = sorted(list(freq.values()))
        value = list(freq.values())
        exist = set()
        ans = 0
        for num in value:
            if num not in exist:
                exist.add(num)
            else:
                while num in exist and num>0:
                    num -= 1
                    ans += 1
                exist.add(num)
        return ans

Explain

该题解的核心策略是使用一个散列表(Counter)来计算字符串中每个字符出现的频次。接着,遍历这些频次,并使用一个集合(set)来存储已经遇到的频次。如果当前的频次已经在集合中存在,则说明有两个字符出现了相同的频次,需要通过逐步减少当前频次(即删除字符),来找到一个未使用的频次,直到找到或频次降至0。每减少一次频次,计数变量(ans)增加1,代表一次删除操作。最后,返回ans即可,它代表了把字符串转变为优质字符串所需的最小删除次数。

时间复杂度: O(n)

空间复杂度: O(n)

class Solution:
    def minDeletions(self, s: str) -> int:
        freq = Counter(s)  # 计算每个字符的频次
        value = list(freq.values())  # 获取频次列表
        exist = set()  # 存储已经存在的频次
        ans = 0  # 初始化删除次数计数器
        for num in value:  # 遍历所有频次
            if num not in exist:  # 如果频次未出现过
                exist.add(num)  # 添加到集合中
            else:  # 如果频次已经存在
                while num in exist and num>0:  # 寻找一个未使用的频次
                    num -= 1  # 减少频次
                    ans += 1  # 增加删除计数
                exist.add(num)  # 添加新的频次到集合
        return ans  # 返回最小删除次数

Explore

在解法中,我通过遍历字符频次列表来确定哪些频次需要被减少。当发现某个频次已存在于集合中时,这表明至少有两个字符的频次相同,此时需要减少当前的频次,以确保所有字符的频次都是唯一的。减少频次的过程不是随机的,而是从当前频次开始逐步减少,直到找到一个未被占用的频次或者频次减至0。这个过程保证了最小化删除操作,以达到所有字符频次唯一的目的。

散列表(Counter)在本解法中用于统计每个字符出现的频次,这为我们提供了一个字符与其频次的映射关系。集合(set)则用于存储已经存在的频次,帮助我们检测某个频次是否已被占用。在实施算法时,我们首先用散列表计算出所有字符的频次,然后遍历这些频次,利用集合来确定哪些频次是独一无二的。如果发现重复的频次,我们通过逐步减少的方式调整频次,直到找到一个未被占用的频次,期间利用集合来持续跟踪已使用的频次。这种方法确保了处理频次的高效性和准确性。

当字符频次通过逐步减少最终减至0时,实际上表示完全删除了该字符的所有出现。在这种情况下,0频次不会被添加到集合中。添加0到集合中没有意义,因为0频次不会对其他字符频次的唯一性构成影响。因此,我们只关注那些大于0的频次,并确保它们唯一。

在本题的解法中,删除操作仅基于字符的频次进行,并没有考虑字符本身的出现顺序或其他属性。目标是达到每个字符的频次都是唯一的,因此只需关注频次的调整。这意味着字符的具体类型或在字符串中的位置不影响删除过程。仅通过调整频次即可满足题目要求,使得每个字符的频次唯一。