目录
题目描述
示例1:
示例2:
提示:
解题思路
滑动窗口法
概念
应用场景及特点:
思路
流程展示
代码
复杂度分析
题目描述
给你一个整数数组nums
和一个整数k
。请你从nums
中满足下述条件的全部子数组中找出最大子数组和:
- 子数组的长度是
k
,且 - 子数组中的所有元素各不相同。
返回满足题面要求的最大子数组和。如果不存在子数组满足这些条件,返回0
。
子数组是数组中一段连续非空的元素序列。
示例1:
输入:nums = [1,5,4,2,9,9,9], k = 3
输出:15
解释:nums 中长度为 3 的子数组是:
- [1,5,4] 满足全部条件,和为 10 。
- [5,4,2] 满足全部条件,和为 11 。
- [4,2,9] 满足全部条件,和为 15 。
- [2,9,9] 不满足全部条件,因为元素 9 出现重复。
- [9,9,9] 不满足全部条件,因为元素 9 出现重复。
因为 15 是满足全部条件的所有子数组中的最大子数组和,所以返回 15 。
示例2:
输入:nums = [4,4,4], k = 3
输出:0
解释:nums 中长度为 3 的子数组是:
- [4,4,4] 不满足全部条件,因为元素 4 出现重复。
因为不存在满足全部条件的子数组,所以返回 0 。
提示:
- 1 <= k <= nums.length <= 105
- 1 <= nums[i] <= 105
解题思路
滑动窗口法
概念
滑动窗口是一个在序列上移动的区间,通常由左右两个指针来界定这个区间的范围。通过移动指针来改变窗口的大小和位置,在窗口移动的过程中,根据问题的需求进行特定的计算和处理。
应用场景及特点:
- 子数组 / 子串问题:
- 当需要在一个序列中找到满足特定条件的连续子数组或子串时,滑动窗口非常适用。例如,寻找和为特定值的连续子数组、含有特定字符的最长子串等。
- 窗口的大小通常是动态变化的,根据问题的条件进行调整。
- 高效性:
- 相比于暴力枚举所有可能的子数组 / 子串,滑动窗口法通常能够在更短的时间内找到解。因为它利用了子数组 / 子串的连续性和窗口的滑动特性,避免了重复计算。
- 指针移动规则:
- 通常有两个指针,一个指向窗口的左端,一个指向窗口的右端。根据问题的具体要求,以特定的方式移动指针。
- 例如,在寻找满足特定条件的最小子数组时,可能会先扩大窗口直到满足条件,然后再缩小窗口以找到最小的满足条件的窗口。
思路
- 初始化:
- 使用一个滑动窗口,窗口大小为
k
。 - 创建一个计数器(可以使用
collections.Counter
)来记录窗口中元素的出现次数。 - 初始化当前窗口的和为 0,最大子数组和为 0。
- 滑动窗口遍历:
- 首先,将窗口的前
k
个元素加入窗口,并计算它们的和以及使用计数器记录元素出现次数。 - 检查窗口中的元素是否各不相同。如果是,更新最大子数组和为当前窗口的和。
- 然后,向右滑动窗口,每次将新元素加入窗口,将离开窗口的元素从计数器中移除,并更新窗口的和。
- 再次检查窗口中的元素是否各不相同。如果是,与当前最大子数组和比较并更新。
- 返回结果:
- 遍历完整个数组后,返回最大子数组和。
流程展示
代码
class Solution:
def maximumSubarraySum(self, nums: List[int], k: int) -> int:
ans = 0
cnt = Counter(nums[:k-1])
s = sum(nums[:k-1])
for in_,out in zip(nums[k-1:],nums):
cnt[in_] += 1
s += in_
if len(cnt) == k:
ans = max(ans, s)
cnt[out] -= 1
if cnt[out] == 0:
del cnt[out]
s -= out
return ans
复杂度分析
- 时间复杂度:由于只需要对数组进行一次遍历,时间复杂度为
O(n)
,其中n
是数组的长度。 - 空间复杂度:使用了计数器和有限的几个变量,空间复杂度为
O(k)
,其中k
是窗口的大小。