leetcode
leetcode 2751 ~ 2800
成为 K 特殊字符串需要删除的最少字符数

成为 K 特殊字符串需要删除的最少字符数

难度:

标签:

题目描述

You are given a string word and an integer k.

We consider word to be k-special if |freq(word[i]) - freq(word[j])| <= k for all indices i and j in the string.

Here, freq(x) denotes the frequency of the character x in word, and |y| denotes the absolute value of y.

Return the minimum number of characters you need to delete to make word k-special.

 

Example 1:

Input: word = "aabcaba", k = 0

Output: 3

Explanation: We can make word 0-special by deleting 2 occurrences of "a" and 1 occurrence of "c". Therefore, word becomes equal to "baba" where freq('a') == freq('b') == 2.

Example 2:

Input: word = "dabdcbdcdcd", k = 2

Output: 2

Explanation: We can make word 2-special by deleting 1 occurrence of "a" and 1 occurrence of "d". Therefore, word becomes equal to "bdcbdcdcd" where freq('b') == 2, freq('c') == 3, and freq('d') == 4.

Example 3:

Input: word = "aaabaaa", k = 2

Output: 1

Explanation: We can make word 2-special by deleting 1 occurrence of "b". Therefore, word becomes equal to "aaaaaa" where each letter's frequency is now uniformly 6.

 

Constraints:

  • 1 <= word.length <= 105
  • 0 <= k <= 105
  • word consists only of lowercase English letters.

代码结果

运行时间: 67 ms, 内存: 16.3 MB


/*
 * 思路:
 * 1. 使用Java Stream API统计每个字符的频率。
 * 2. 将这些频率进行排序。
 * 3. 使用滑动窗口检查在给定k值的范围内,删除最少字符后能使得所有字符的频率差不超过k。
 */
import java.util.*;
import java.util.stream.Collectors;

public class KSpecialStringStream {
    public int minDeletions(String word, int k) {
        Map<Character, Long> frequencyMap = word.chars()
                .mapToObj(c -> (char) c)
                .collect(Collectors.groupingBy(c -> c, Collectors.counting()));
        List<Long> frequencies = frequencyMap.values().stream().sorted().collect(Collectors.toList());

        int minDeletions = Integer.MAX_VALUE;
        for (int i = 0; i < frequencies.size(); i++) {
            int deletions = 0;
            for (int j = 0; j < frequencies.size(); j++) {
                if (Math.abs(frequencies.get(i) - frequencies.get(j)) > k) {
                    deletions += Math.abs(frequencies.get(i) - frequencies.get(j)) - k;
                }
            }
            minDeletions = Math.min(minDeletions, deletions);
        }
        return minDeletions;
    }

    public static void main(String[] args) {
        KSpecialStringStream solver = new KSpecialStringStream();
        System.out.println(solver.minDeletions("aabcaba", 0)); // 输出:3
        System.out.println(solver.minDeletions("dabdcbdcdcd", 2)); // 输出:2
        System.out.println(solver.minDeletions("aaabaaa", 2)); // 输出:1
    }
}

解释

方法:

该题解首先统计了字符串中每个字符的频率,并将频率进行排序。对于每个可能的目标频率(从最大频率递减到最小频率),算法尝试确定以该频率为边界时,将所有大于此频率加k的字符频率减少到该边界,所有小于此频率的字符频率提升到该边界需要删除的最少字符数。通过右指针从高频向低频移动,计算每个位置需要删除的字符总数,并不断更新最小删除数。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
题解中提到的'将所有大于此频率加k的字符频率减少到该边界,所有小于此频率的字符频率提升到该边界',这种操作是否考虑了实际可能存在的字符数量限制,即是否可能存在字符不足以提升到目标频率的情况?
题解的算法在实际执行时并没有直接增加字符的总数,而是通过计算需要删除的最少字符数来模拟这个过程。即使实际上不可能有足够的字符来提升到目标频率,算法所做的是通过计算如果要达到那个频率需要删除多少字符来达成条件。因此,算法主要关注的是达到一个平衡状态下需要删除的字符数量,而非字符数量是否足够。
🦆
在题解的算法中,right 指针的调整过程是否考虑了所有字符频率等于目标频率加k的情况,这些情况下的字符是否需要移动指针和调整删除数?
题解中的算法设计是使 right 指针停在第一个小于或等于目标频率的位置。因此,如果有字符的频率正好等于目标频率加k,这些字符的频率不需要调整,也不会影响删除数的计算。right 指针在这种情况下会停在这些字符的频率之前的位置,只关注那些频率高于目标频率加k的字符。
🦆
算法为何选择从最高频率递减至最低频率遍历作为初始点,而不是从最低频率开始或其他可能的方法?
从最高频率到最低频率遍历可以更快地找到需要删除字符最少的情况。考虑到通常较高频率的字符在减少到较低频率时需要删除的字符数量会更多,从高到低遍历可以快速地评估减少这些高频字符带来的效果,并逐步考虑增加低频字符的影响。这种方法比从低频率开始更高效,因为从低频率开始则需要频繁考虑如何减少高频字符,这在初期可能导致大量不必要的删除操作。

相关问题