leetcode
leetcode 2151 ~ 2200
每种字符至少取 K 个

每种字符至少取 K 个

难度:

标签:

题目描述

You are given a string s consisting of the characters 'a', 'b', and 'c' and a non-negative integer k. Each minute, you may take either the leftmost character of s, or the rightmost character of s.

Return the minimum number of minutes needed for you to take at least k of each character, or return -1 if it is not possible to take k of each character.

 

Example 1:

Input: s = "aabaaaacaabc", k = 2
Output: 8
Explanation: 
Take three characters from the left of s. You now have two 'a' characters, and one 'b' character.
Take five characters from the right of s. You now have four 'a' characters, two 'b' characters, and two 'c' characters.
A total of 3 + 5 = 8 minutes is needed.
It can be proven that 8 is the minimum number of minutes needed.

Example 2:

Input: s = "a", k = 1
Output: -1
Explanation: It is not possible to take one 'b' or 'c' so return -1.

 

Constraints:

  • 1 <= s.length <= 105
  • s consists of only the letters 'a', 'b', and 'c'.
  • 0 <= k <= s.length

代码结果

运行时间: 152 ms, 内存: 16.4 MB


// Solution in Java using Java Streams
// This solution uses Java Streams to count characters and determine the minimum minutes.

import java.util.stream.IntStream;

public class Solution {
    public int minMinutes(String s, int k) {
        int n = s.length();
        int[] left = new int[3];
        int[] right = new int[3];
        int l = 0, r = n - 1, total = 0;
        while (l <= r) {
            // Count left side
            if (l < n) {
                left[s.charAt(l) - 'a']++;
                l++;
                if (IntStream.of(left).allMatch(x -> x >= k)) break;
            }
            // Count right side
            if (r >= 0) {
                right[s.charAt(r) - 'a']++;
                r--;
                if (IntStream.of(right).allMatch(x -> x >= k)) break;
            }
        }
        // Check if we can meet the requirement
        if (IntStream.of(left).anyMatch(x -> x < k) || IntStream.of(right).anyMatch(x -> x < k)) return -1;
        // Calculate the minimum moves
        total = (l + (n - r - 1));
        return total;
    }
}

解释

方法:

题解采用滑动窗口的方法来找出最短的子数组,使得移除这个子数组后剩余的字符串中每个字符的数量都至少为k。首先计算字符串中每个字符'a'、'b'、'c'的总数减去k,得到a、b、c。如果任何一个字符的总数不足k,则直接返回-1,因为无法满足条件。接着使用一个滑动窗口(通过两个指针i和j定义),遍历字符串,统计窗口中各个字符的数量。如果窗口中某个字符的数量超过了所需的最小数量(比如'a'超过a),则尝试缩小窗口(移动左指针i)以找到最小的满足条件的窗口。最后,计算除去这个窗口外的剩余部分的长度,即为所需的最少分钟数。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
如何确定滑动窗口中每个字符的最小数量阈值,即变量a、b、c是如何计算得出的?
在这个问题中,变量a、b、c代表除去至少k个各字符后,剩余可以用于构成滑动窗口的字符数量。具体地,对于每种字符('a'、'b'、'c'),我们先计算该字符在整个字符串中的总数,然后减去k。这样,得到的结果就是滑动窗口在理想情况下可以容纳的该字符的最大数量。如果某字符的总数小于k,那么将这个值设为负数,表示不可能构成有效的滑动窗口,因为不能满足每种字符至少有k个的条件。
🦆
题解中提到如果某个字符的总数不足k,则返回-1。这个判断是否在整个字符串被遍历前就可以确定?
是的,这个判断可以在遍历字符串之前就确定。在开始滑动窗口算法之前,我们首先计算字符串中每种字符的总数量,并检查每种字符的数量是否至少为k。如果发现任何一个字符的总数少于k,则可以立即返回-1,因为无论如何也无法通过移除部分字符来满足每种字符至少有k个的条件。这个判断是预处理步骤,有助于提前结束算法,避免不必要的计算。
🦆
题解中使用的滑动窗口策略是尝试找到最小的窗口使得移除这个窗口后剩余部分的每种字符都至少有k个。请问为什么不直接从字符串的两端开始移除字符来尝试满足条件?
从字符串的两端开始移除字符虽然直观,但可能不会得到最优解。这种方法可能会移除过多的符合条件的字符,特别是当符合条件的字符集中在字符串的中部时。相比之下,滑动窗口方法能够更灵活地调整窗口的大小和位置,寻找到确切的最小的可移除窗口,从而使剩余的字符串部分满足每种字符至少有k个的条件。这种方法更为有效,因为它考虑了字符在整个字符串中的分布,而不仅仅是从两端考虑。
🦆
在滑动窗口中,为什么在确定窗口内某个字符的数量超过所需时,就缩小窗口而不是继续拓展窗口?
在滑动窗口算法中,目标是找到最小的窗口,使得移除这个窗口后剩余的字符串部分的每种字符数量都至少为k。当窗口内某个字符的数量超过所需的最小数量时,继续增大窗口不会帮助改进结果,反而可能包含更多不需要移除的字符,导致不满足条件或不是最优解。因此,我们尝试缩小窗口,以确保窗口是最小的,并且剩余的每种字符的数量仍满足条件。这样的策略有助于精确控制窗口大小,确保找到最优解。

相关问题