日常系列LeetCode《12·滑动窗口篇》
Posted 常某某的好奇心
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了日常系列LeetCode《12·滑动窗口篇》相关的知识,希望对你有一定的参考价值。
数据规模->时间复杂度
<=10^4 😮(n^2)
<=10^7:o(nlogn)
<=10^8:o(n)
10^8<=:o(logn),o(1)
总结
伪代码
#最长窗口
int left = 0, right = 0;
ans=map,set,变量;
while (right < nums.length)
处理 right 指向的元素,将其加入到当前窗口
while (窗口满足条件)
// 计算结果
处理 left 指向的元素,将其从当前窗口中剔除掉
left++;
// 计算结果
right++;
#最短窗口
int left = 0, right = 0;
ans=map,set,变量;
while (right < nums.length)
处理 right 指向的元素,将其加入到当前窗口
while (窗口不满足条件)
// 计算结果
处理 left 指向的元素,将其从当前窗口中剔除掉
left++;
// 计算结果
right++;
lc 643 :子数组最大平均数 I
https://leetcode.cn/problems/maximum-average-subarray-i/
提示:
n == nums.length
1 <= k <= n <= 10^5
-10^4 <= nums[i] <= 10^4
#方案一:暴力解法存在重复计算
#方案二:前缀和(空间换时间)
class Solution:
def findMaxAverage(self, nums: List[int], k: int) -> float:
n=len(nums)
#o(n)
prefixsum=[0]*(n+1)
for i in range(1,n+1):
prefixsum[i]=prefixsum[i-1]+nums[i-1]
#o(n)
maxv=float('-inf')
for i in range(n-k+1):
sum1=prefixsum[i+k]-prefixsum[i]
maxv=max(maxv,sum1)
#
return maxv/k
#方案三:滑动窗口
class Solution:
def findMaxAverage(self, nums: List[int], k: int) -> float:
#o(1)
n=len(nums)
maxv=float('-inf')
currwindowsum=0
#o(2n)
left=right=0
while right<n:
currwindowsum+=nums[right]
#更新
if right-left+1==k:
maxv=max(maxv,currwindowsum)
currwindowsum-=nums[left]
left+=1
right+=1
#
return maxv/k
lc 209【剑指 008】:长度最小的子数组
https://leetcode.cn/problems/minimum-size-subarray-sum/
提示:
1 <= target <= 10^9
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^5
进阶:
如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。
#方案一:滑动窗口
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
minl=inf=float('inf')
currwindowsum=0
#
left=right=0
while right<len(nums):
currwindowsum+=nums[right]
#更新:注意while,非if
while currwindowsum>=target:
minl=min(minl,right-left+1)
currwindowsum-=nums[left]
left+=1
right+=1
#
return minl if minl!=inf else 0
#方案二:前缀和(注:升序)+二分法
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
#
minl=inf=float('inf')
prefixsum=[0]*(len(nums)+1)
for i in range(1,len(nums)+1):
prefixsum[i]=prefixsum[i-1]+nums[i-1]
#
for i in range(len(nums)+1):
t=target+prefixsum[i]
j=self.findplace(prefixsum,t)
if j==-1:continue
minl=min(minl,j-i) #注意: pre[j]-pre[i]=nums.sum[i,j)>=target,个数:j-i
#
return minl if minl!=inf else 0
#o(nlogn)
def findplace(self,nums,target):
left=0
right=len(nums)-1
while left<=right:
mid=left+(right-left)//2
if nums[mid]>=target:
if mid==0 or nums[mid-1]<target:return mid
else:right=mid-1
else:left=mid+1
#
return -1
lc 3【剑指 016】【top100】:无重复字符的最长子串
https://leetcode.cn/problems/longest-substring-without-repeating-characters/
提示:
0 <= s.length <= 5 * 10^4
s 由英文字母、数字、符号和空格组成
#方案一:滑动窗口+HashSet
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
#
n=len(s)
if n<=1:return n
#o(n),o(2n)
maxl=1
left=right=0
hashset=set()
while right<n:
#写法1
while s[right] in hashset:
hashset.remove(s[left])
left+=1
maxl=max(maxl,right-left+1)
hashset.add(s[right])
right+=1
#写法2
# if s[right] not in hashset:
# maxl=max(maxl,right-left+1)
# hashset.add(s[right])
# right+=1
# else:
# #key:因为之前s[left]已经被统计过了,所以这里通过去s[left]来缩小窗口
# hashset.remove(s[left])
# left+=1
#
return maxl
#方案二:滑动窗口+HashMap
'''
#以上当在窗口中存在重复字符,是一个一个字符的缩小窗口
#我们可以通过记住每个字符在字符串中的索引,当遇到重复字符的时候,就可以直接跳到重复字符的后面
'''
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
#
n=len(s)
if n<=1:return n
#o(n),o(n)
maxl=1
left=right=0
hashmap= #用于判重
while right<n:
#更新
#key:有重复,left则跳到重复元素对应索引后一位;无重复,left不变->一直在维护各种无重复子串
left=max(hashmap.get(s[right],-1)+1,left)
maxl=max(maxl,right-left+1
#
hashmap[s[right]]=right #注:重复元素会再次被更新索引
right+=1
#
return maxl
#方案三:使用数组代替HashMap
'''
// s 由英文字母、数字、符号和空格组成
'''
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
#
n=len(s)
if n<=1:return n
#o(1),o(n)
maxl=1
left=right=0
arr=[-1]*128 #ASSIC码0-127:128个字符(英文字母、数字、符号和空格) #作用:判重
while right<n:
#1)key:有重复,left则跳到重复元素对应索引后一位;无重复,left不变
left=max(arr[ord(s[right])]+1,left)
#2)非重复式更新
maxl=max(maxl,right-left+1)
#
arr[ord(s[right])]=right #相同元素索引,一直被维护和更新
right+=1
#
return maxl
lc 76【top100】:最小覆盖子串
https://leetcode.cn/problems/minimum-window-substring/
提示:
1 <= s.length, t.length <= 105
s 和 t 由英文字母组成
进阶:
你能设计一个在 o(n) 时间内解决此问题的算法吗?
#方案一:滑动窗口
class Solution:
def minWindow(self, s: str, t: str) -> str:
#s 和 t 由英文字母组成
cnt_t=[0]*60 #'''标记1单元素个数'''
singlesumT=0 #'''标记2非重复个数'''
for c in t:
if cnt_t[ord(c)-ord('A')]==0:
singlesumT+=1
cnt_t[ord(c)-ord('A')]+=1
#o(1),o(2n)
left=right=0
cnt_window=[0]*60 #'''标记3单元素个数'''
singlesumW=0 #'''标记4非重复个数'''
minlen=[-1,0,0] #'''标记5最小长度,对应子串的left、right'''
while right<len(s):
cnt_window[ord(s[right])-ord('A')]+=1
if cnt_window[ord(s[right])-ord('A')]==cnt_t[ord(s[right])-ord('A')]:
singlesumW+=1
#更新->寻最小
while singlesumW==singlesumT:
if minlen[0]==-1 or right-left+1<minlen[0]:
minlen[0]=right-left+1
minlen[1]=left
minlen[2]=right
cnt_window[ord(s[left])-ord('A')]-=1
if cnt_window[ord(s[left])-ord('A')]<cnt_t[ord(s[left])-ord('A')]:
singlesumW-=1
left+=1
right+=1
#
return s[minlen[1]:minlen[2]+1] if minlen[0]!=-1 else ''
lc 485 :最大连续 1 的个数
https://leetcode.cn/problems/max-consecutive-ones/
提示:
1 <= nums.length <= 10^5
nums[i] 不是 0 就是 1.
#方案一:线性遍历
class Solution:
def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
#o(1),o(n)
ones=maxl=0
#写法1
for n in nums:
if n==1:
ones+=1
maxl=max(maxl,ones)
else:
ones=0
return maxl
#写法2
# if n==1:
# ones+=1
# else:
# maxl=max(maxl,ones) #bug:遇0才更新,如果后面都是零,将得不到更新
# ones=0
# #
# return max(maxl,ones) #[1,1,0,1,1,1]
#方案二:滑动窗口
class Solution:
def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
#o(1),o(n)
maxl=0
left=right=0
#写法1
while right<len(nums):
if nums[right]==1:
maxl=max(maxl,right-left+1)
else:
left=right+1
right+=1
return maxl
#写法2
# while right<len(nums):
# if nums[right]==0:
# maxl=max(maxl,right-left)
# left=right+1
# right+=1
# return max(maxl,right-left)
lc 487 :最大连续1的个数 II
https://leetcode.cn/problems/max-consecutive-ones-ii/
提示:
1 <= nums.length <= 10^5
nums[i] 不是 0 就是 1.
进阶:
如果输入的数字是作为 无限流 逐个输入如何处理?换句话说,内存不能存储下所有从流中输入的数字。您可以有效地解决吗?
#方案一:滑动窗口
class Solution:
def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
maxl=0
left=right=0
currwin=0
while right<len(nums):
if nums[right]==0:
currwin+=1
#更新
if currwin==2:
maxl=max(maxl,right-left)
#浓缩:这种方法需全放内存
while currwin==2:
if nums[left]==0:currwin-=1
left+=1
right+=1
return max(maxl,right-left)
#方案二:解决无限流->标记位置
class Solution:
def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
maxl=0
left=right=0
zeroIndex=-1 #记录当前窗口0所在位置
while right<len(nums):
#更新
if nums[right]==0:
if zeroIndex>=0: #说明当前窗口已经有0
maxl=max(maxl,right-left)
left=zeroIndex+1
#浓缩
zeroIndex=right
right+=1
return max(maxl,right-left)
lc 1004 :最大连续 1 的个数 III
https://leetcode.cn/problems/max-consecutive-ones-iii/
提示:
1 <= nums.length <= 10^5
nums[i] 不是 0 就是 1
0 <= k <= nums.length
#方案一:滑动窗口
class Solution:
def longestOnes(self, nums: List[int], k: int) -> int:
maxl=0
left=right=0
currzerosum=0
while right<len(nums):
if nums[right]==0:
currzerosum+=1
if currzerosum==k+1:
maxl=max(maxl,right-left)
#缩
while currzerosum==k+1:
if nums[left]==0:currzerosum-=1
left+=1
#
right+=1
#
return max(maxl,right-left)
lc 1151 :最少交换次数来组合所有的 1
https://leetcode.cn/problems/minimum-swaps-to-group-all-1s-together/
提示:
1 <= data.length <= 10^5
data[i] == 0 or 1.
'''
‘聚合1’=>问题转变为:满足窗口大小为k时,统计最小为0的个数(即需要交换个数)
'''
#方案一:滑动窗口
class Solution:
def minSwaps(self, data: List[int]) -> int:
#统计1个数
k=0
k=sum(c for c in data if c==1)
# for c in data:
# if c==1:k+=1
#o(1),o(n)
left=right=0
currwinzero=0
minzerosum=inf=float('inf') #bug:若 minzerosum=0,data=[1,0,1,0,1]->minzerosum=max(1,0)=0,错误
while right<len(data):
if data[right]==0:
currwinzero+=1
#更新
if right-left+1==k:
minzerosum=min(minzerosum,currwinzero)
if data[left]==0:currwinzero-=1
left+=1
right+=1
return minzerosum if minzerosum!=inf else 0
lc 30 :串联所有单词的子串
https://leetcode.cn/problems/substring-with-concatenation-of-all-words/
提示:
1 <= s.length <= 10^4
s 由小写英文字母组成
1 <= words.length <= 5000
1 <= words[i].length <= 30
wor以上是关于日常系列LeetCode《12·滑动窗口篇》的主要内容,如果未能解决你的问题,请参考以下文章
leetcode 1869. 哪种连续子字符串更长---滑动窗口篇3,双指针篇4
leetcode 3. 无重复字符的最长子串----滑动窗口篇1,双指针篇1