难度: Medium
Submission
运行时间: 144 ms
内存: 26.9 MB
class Solution: def beautifulBouquet(self, flowers: List[int], cnt: int) -> int: MOD = 10 ** 9 + 7 l, ans = -1, 0 dic = defaultdict(int) for i, x in enumerate(flowers): dic[x] += 1 while dic[x] > cnt: l += 1 dic[flowers[l]] -= 1 ans += i - l ans %= MOD return ans
Explain
这个题解使用了滑动窗口(双指针)的方法来寻找所有满足条件的连续子数组(美观的花束)。维护两个指针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)
class Solution: def beautifulBouquet(self, flowers: List[int], cnt: int) -> int: MOD = 10 ** 9 + 7 # 防止结果溢出的模数 l, ans = -1, 0 # 初始化左指针l和答案ans dic = defaultdict(int) # 记录窗口中每种花的数量 for i, x in enumerate(flowers): # 遍历每朵花 dic[x] += 1 # 增加当前花品种x的计数 while dic[x] > cnt: # 当当前品种数量超过限制时,移动左指针 l += 1 dic[flowers[l]] -= 1 # 更新左指针指向花的计数 ans += i - l # 计算当前位置结尾的所有美观花束数,并累加到结果中 ans %= MOD # 取模保证不溢出 return ans # 返回结果
Explore
在双指针方法中,左指针`l`初始设为`-1`是为了逻辑上方便处理子数组的开始边界。当`l`从`-1`开始,`i - l`的结果直接表示从`l + 1`到`i`的区间长度,即当前有效窗口的大小。如果从`0`开始,则在进行第一次计算时会需要特殊处理,以确保区间长度计算正确。使用`-1`作为起始点,简化了代码逻辑,使得每次增加`i`时,直接使用`i - l`能够正确反映新的窗口长度。
当`dic[x] > cnt`时,说明当前花品种`x`的数量已经超过了允许的最大值`cnt`。移动左指针`l`一次是为了逐步减少包含过多`x`的花品种的窗口范围。每次只移动一次是因为我们需要从当前不满足条件的最小窗口开始逐步检查,通过逐个剔除左边界的花,直到窗口中的该花品种数量不超过`cnt`。这种方法确保了每个可能的子数组都被逐一考察,从而不遗漏任何一个美观花束的计算。
在这个算法中,`dic`哈希表确实记录了窗口中每种花的数量,但是这里不存在重复计数的问题。因为算法的目的是计算所有可能的`美观的花束`,即每次窗口调整(无论是扩大还是缩小)都是为了寻找新的有效的花束配置。每次当`i`增加时,计算的是以`i`为结束点的所有有效花束数,这些花束是基于当前窗口状态的合法配置。每次左指针`l`移动时,之前可能的重叠计数已经在之前的迭代中被处理过,当前计数只关注新的有效区间,因此不会有重复计数的问题。