滑动窗口
滑动窗口
滑动窗口
209 长度最小的子数组:leetcode.cn/problems/minimum-size-subarray-sum/
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl+1, ..., numsr-1, numsr],并返回其长度。如果不存在符合条件的子数组,返回 0 。
思路与解答(可变长窗口,最小化长度)
关键条件:nums 全为正数,因此当我们右扩时窗口和单调不减;当我们左缩时窗口和单调不增。这使得双指针可以线性推进。
不变量设计:
- 维护窗口
[l, r]的区间和s。 - 当
s >= target时,尝试不断右移l来缩短窗口,同时更新最优解。
1
2
3
4
5
6
7
8
9
10
11
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
ans = inf
s, l = 0, 0
for r, x in enumerate(nums):
s += x
while s >= target:
ans = min(ans, r - l + 1)
s -= nums[l]
l += 1
return ans if ans <= len(nums) else 0
复杂度分析
- 时间复杂度:
O(n),l、r都最多移动n次。 - 空间复杂度:
O(1)。
713 乘积小于 K 的子数组:leetcode.cn/problems/subarray-product-less-than-k
给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目。
思路与解答(可变长窗口,计数型)
关键条件:nums[i] 为正整数时,窗口乘积在右扩时单调不减;左缩会单调不增,因此可用双指针维护“窗口乘积 < k”。
计数技巧:
- 设当前窗口为
[l, r]且满足product < k。 - 那么所有以
r结尾的合法子数组个数为:r - l + 1(起点可选l..r)。
边界:当 k <= 1 时,正整数乘积不可能 < k,直接返回 0。
1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
if k <= 1:
return 0
ans, s, l = 0, 1, 0
for r, x in enumerate(nums):
s *= nums[r]
while s >= k and l <= r:
s //= nums[l]
l += 1
ans += r - l + 1
return ans
复杂度分析
- 时间复杂度:
O(n)。 - 空间复杂度:
O(1)。
3 无重复字符的最长字串:leetcode.cn/problems/longest-substring-without-repeating-characters
给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。
思路与解答(可变长窗口,最大化长度)
不变量设计:
- 维护窗口
[l, r]内“所有字符出现次数 ≤ 1”。 - 右扩加入
s[r]后若出现重复,则不断左缩直至恢复不变量。 - 每次窗口合法时更新最大长度
ans = max(ans, r - l + 1)。
1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
ans = 0
cnt = Counter()
l = 0
for r, c in enumerate(s):
cnt[c] += 1
while cnt[c] >= 2:
cnt[s[l]] -= 1
l += 1
ans = max(ans, r - l + 1)
return ans
This post is licensed under CC BY 4.0 by the author.