leetcode
leetcode 3001 ~ 3050
美观的花束

美观的花束

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 144 ms, 内存: 26.9 MB


/*
 * 思路:使用Java Stream来实现这个算法需要一些变通的方法,因为Stream并不直接支持滑动窗口操作。
 * 我们可以先将数据进行处理,然后再使用Stream来进行结果的计算。
 */
import java.util.HashMap;
import java.util.stream.IntStream;

public class BeautifulBouquetStream {
    public int countBeautifulBouquets(int[] flowers, int cnt) {
        final int MOD = 1000000007;
        int n = flowers.length;
        HashMap<Integer, Integer> countMap = new HashMap<>();
        int[] resultArray = new int[n];

        int left = 0;
        for (int right = 0; right < n; right++) {
            countMap.put(flowers[right], countMap.getOrDefault(flowers[right], 0) + 1);
            while (countMap.get(flowers[right]) > cnt) {
                countMap.put(flowers[left], countMap.get(flowers[left]) - 1);
                left++;
            }
            resultArray[right] = right - left + 1;
        }

        return IntStream.of(resultArray).reduce(0, (sum, value) -> (sum + value) % MOD);
    }

    public static void main(String[] args) {
        BeautifulBouquetStream solution = new BeautifulBouquetStream();
        int[] flowers1 = {1, 2, 3, 2};
        int cnt1 = 1;
        System.out.println(solution.countBeautifulBouquets(flowers1, cnt1)); // 输出:8

        int[] flowers2 = {5, 3, 3, 3};
        int cnt2 = 2;
        System.out.println(solution.countBeautifulBouquets(flowers2, cnt2)); // 输出:8
    }
}

解释

方法:

这个题解使用了滑动窗口(双指针)的方法来寻找所有满足条件的连续子数组(美观的花束)。维护两个指针i和l,其中i是当前考察的花的位置,而l是使得从l到i的子数组满足每种花的数量都不超过cnt的最小可能的起点。使用一个哈希表dic来记录窗口中每种花的数量。遍历flowers数组,对于每个花品种x,增加dic[x]的计数。如果dic[x]超过了cnt,说明当前窗口从l到i不再美观,因此需要将l右移直到dic[x]不超过cnt。每次右移l后,相应地减少dic[flowers[l]]的计数。每个有效的l到i的窗口都是一个美观的花束,i - l表示以i为结束位置的美观花束的数量。结果ans累加这些花束的数量,并且使用MOD来防止溢出。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在使用双指针法时,如何确定初始的左指针`l`的位置,为什么从`-1`开始而不是从`0`开始?
在双指针方法中,左指针`l`初始设为`-1`是为了逻辑上方便处理子数组的开始边界。当`l`从`-1`开始,`i - l`的结果直接表示从`l + 1`到`i`的区间长度,即当前有效窗口的大小。如果从`0`开始,则在进行第一次计算时会需要特殊处理,以确保区间长度计算正确。使用`-1`作为起始点,简化了代码逻辑,使得每次增加`i`时,直接使用`i - l`能够正确反映新的窗口长度。
🦆
请解释为什么每次当`dic[x] > cnt`时,只移动左指针`l`一次就足够了,这是否确保了所有可能的美观花束都被考虑?
当`dic[x] > cnt`时,说明当前花品种`x`的数量已经超过了允许的最大值`cnt`。移动左指针`l`一次是为了逐步减少包含过多`x`的花品种的窗口范围。每次只移动一次是因为我们需要从当前不满足条件的最小窗口开始逐步检查,通过逐个剔除左边界的花,直到窗口中的该花品种数量不超过`cnt`。这种方法确保了每个可能的子数组都被逐一考察,从而不遗漏任何一个美观花束的计算。
🦆
哈希表`dic`中记录了每种花的数量,但是如何处理在最后的输出结果中可能存在的重复计数问题?
在这个算法中,`dic`哈希表确实记录了窗口中每种花的数量,但是这里不存在重复计数的问题。因为算法的目的是计算所有可能的`美观的花束`,即每次窗口调整(无论是扩大还是缩小)都是为了寻找新的有效的花束配置。每次当`i`增加时,计算的是以`i`为结束点的所有有效花束数,这些花束是基于当前窗口状态的合法配置。每次左指针`l`移动时,之前可能的重叠计数已经在之前的迭代中被处理过,当前计数只关注新的有效区间,因此不会有重复计数的问题。

相关问题