一、学习视频:滑动窗口【基础算法精讲 03】_哔哩哔哩_bilibili
二、视频跟练代码
1.题目:209. 长度最小的子数组
方法:双指针
代码:
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
# if sum(nums) < target:
# return 0
l = 0
n = len(nums)
cnt = inf
for r in range(n):
while sum(nums[l+1:r+1]) >= target:
#注意这里是l+1不然退出循环,l和r之间和会小于target
l += 1
if sum(nums[l:r+1]) >= target: #才开始时,r-l+1较小,会出错
cnt = min(cnt, r-l+1)
return cnt if cnt <= n else 0
我根据视频的思路写的代码,居然超时了。仔细观察发现,我使用切片求和的话,每一次判断都要求一次,这样时间就长了。于是更改为了和视频一样的用参数保存,修改代码如下:
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
if sum(nums) < target:
return 0
l = 0
n = len(nums)
cnt = inf
sum_ = 0
for r in range(n):
sum_ += nums[r]
while sum_ - nums[l] >= target:
#注意这里-num[l],不然退出循环,l和r之间和会小于target
sum_ -= nums[l]
l += 1
if sum_ >= target: #才开始时,r-l+1较小,会出错
cnt = min(cnt, r-l+1)
或
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
if sum(nums) < target:
return 0
l = 0
n = len(nums)
cnt = inf
sum_ = 0
for r in range(n):
sum_ += nums[r]
# # 代码1
# while sum_ - nums[l] >= target:
# #注意这里-num[l],不然退出循环,l和r之间和会小于target
# sum_ -= nums[l]
# l += 1
# if sum_ >= target: #才开始时,r-l+1较小,会出错
# cnt = min(cnt, r-l+1)
# #
# 代码2
while sum_ >= target: #操作循环一起
cnt = min(cnt, r-l+1) #先更新后变化 #但是这样比代码1慢,因为每一次while循环都要更新一次,而代码1不是
sum_ -= nums[l]
l += 1
#
return cnt # if cnt <= n else 0
注意:这里的时间复杂度是O(n)的,而不是O(n^2)。因为循环次数和L,R移动的次数有关,但是L,R之间是没有关系的;L最多只移动n次,R也是。是相加的关系而不是相乘。
2024.3.17续:
2.题目:713. 乘积小于 K 的子数组
方法:双指针
代码:
class Solution:
def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
if k <= 1: #注意k == 1时,也无法满足
return 0
l = 0
ans = 0
mutiple = 1
for r in range(len(nums)):
mutiple *= nums[r]
while mutiple >= k:
mutiple /= nums[l]
l += 1
# for _ in range(l,r+1): #以r为右端点的数组的所有子数组
# ans += 1
ans += r-l+1
return ans
3.题目:3. 无重复字符的最长子串
方法:滑动窗口双指针
代码:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
# 滑动窗口双指针
n = len(s)
if n <= 1:
return n
ans = -inf
# 字符串储存
chars = ''
l = 0
for r,x in enumerate(s):
while x in chars:
l += 1
chars = chars[1:] #去掉第一个
chars += x #字符串拼接
ans = max(ans,r-l+1)
return ans
还可以使用哈希表储存,时间比用字符串储存快。代码参考视频,代码如下:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
# 滑动窗口双指针
n = len(s)
if n <= 1:
return n
ans = -inf
# 哈希表储存
cnt = Counter() # hashmap key-char,value-count #这里类似初始化,使用Counter函数
l = 0
for r,c in enumerate(s):
cnt[c] += 1
while cnt[c] > 1:
cnt[s[l]] -= 1
l += 1
ans = max(ans,r-l+1)
return ans
时间复杂度O(n)。空间复杂度(重点!!!): O(128)(ASCII码个数);O(1);O(len(set(s))去重后s的个数(根据视频)。
三、课后作业练习
代码:
class Solution:
def maxSubarrayLength(self, nums: List[int], k: int) -> int:
# 滑动窗口
if k <= 0:
return 0
ans = 0
hash = {} #hash表
l = 0
for r,x in enumerate(nums):
hash[x] = hash.get(x,0) + 1
while hash[x] > k: #只要每一个x频率小于等于k,就能满足都小于等于k
hash[nums[l]] -= 1
l += 1
ans = max(ans,r-l+1)
return ans
2.题目: 2730. 找到最长的半重复子字符串
代码:
class Solution:
def longestSemiRepetitiveSubstring(self, s: str) -> int:
# 滑动窗口
# if len(s) == 1: #下标从1开始,要先验证下标为一
# return 1
# ans = 0
ans = 1 #包含长为1时
flag = 0 #标记相等相邻字符的个数
l = 0 #l从0开始,l和r之间的是所需字符串
for r in range(1,len(s)):
if s[r] == s[r-1]:
flag += 1
while flag > 1:
if s[l] == s[l+1]:
flag -= 1
l += 1
ans = max(ans,r-l+1)
return ans
3.题目:1004. 最大连续1的个数 III
代码:
class Solution:
def longestOnes(self, nums: List[int], k: int) -> int:
# 滑动窗口
n = len(nums)
l = 0
ans = 0
cnt = 0
for r,x in enumerate(nums):
# k -= 1-x #为了不改变k
cnt += 1-x
# while k < 0:
while cnt > k:
# if nums[l] == 0:
# cnt -= 1
cnt -= 1-nums[l]
l += 1
ans = max(ans,r-l+1)
return ans
代码:
class Solution:
def countSubarrays(self, nums: List[int], k: int) -> int:
if k > len(nums):
return 0
cnt = 0
ans = 0
maxnum = max(nums)
l = 0
for r,x in enumerate(nums):
if x == maxnum:
cnt += 1
while cnt >= k:
if nums[l] == maxnum:
cnt -= 1
l += 1
ans += l #注意!!右端点不同,数组不同
return ans
代码:
class Solution:
def countSubarrays(self, nums: List[int], k: int) -> int:
# 数组之和大于1递增,数组长度大于1递增,整体是递增的
# 所以是先满足小于k,后大于等于k
# 所以设置while循环条件为大于等于k
ans,l,sum_ = 0,0,0
for r,x in enumerate(nums):
sum_ += x
while sum_ * (r-l+1) >= k:
sum_ -= nums[l]
l += 1
ans += r-l+1
return ans
代码:
class Solution:
def minOperations(self, nums: List[int], x: int) -> int:
#反过来想,就是连续数组和为sum(nums)-x
#最小操作数就是子数组长度最长
# if min(nums) > x:
# return -1
goal = sum(nums) - x
if goal < 0: #注意!
return -1
l,ans,sum_ = 0,-1,0
for r,num in enumerate(nums):
sum_ += num
while sum_ > goal:
sum_ -= nums[l]
l += 1
if sum_ == goal:
ans = max(ans,r-l+1)
return -1 if ans < 0 else len(nums)-ans #数组长度减去子数组长度
完
感谢你看到这里!一起加油吧!