209、长度最小的子数组

解题思路:如果用两层循环选数组长度,时间复杂度会很高,计算量也很大。所以选择一个变量记录最小长度result,sum记录当前窗口的和。之后通过滑动窗口,右边界不断向右扩展,并将右边界加入sum,一旦满足大于等于target条件,就比较当前窗口长度和保存的已知最小长度result进行比较,并取最小值放入result,之后左边界也向右移动,不断缩小窗口,注意在移动前sum减去左边界。之后不断循环判断这个窗口数组是否依旧满足条件。不满足条件就停止缩小左边界,继续扩展右边界。
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
#好习惯,做一个违法检测
if nums is None or len(nums)==0:
return 0
l = len(nums)
result = l + 1
sum = left = 0
for right in range(0, l):
#窗口右端右移
sum += nums[right]
#当已经获取到满足条件的窗口和值时,窗口左端右移找满足条件最小长度
while sum >= target:
result=min(result,right-left+1)
sum -= nums[left]
left+=1
if result>l:return 0
else:return result
3、无重复字符的最长子串

解题思路:这个题目要判断当前子串是否有重复的,可以使用python中的Counter()函数,通过cnt = Counter()可以建立一个计数器,如果访问不存在的元素其不会报错,查询的键不在Counter中,它会返回0,而不是抛出KeyError异常。相当于对任意键都有一个默认值0。因此可以通过其用来对当前字符串中出现的字符进行计数,右边界每遍历一个字符,都会将其对应的值加1,然后判断,若其大于一则说明之前的字符串中有这个字符,就需要移动左窗口直到字符串中没有重复的字符,然后比较当前的字符串长度。当然,要记得移动左窗口时,把左窗口字符对应的数字减去。
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
left = 0
result = 0
cnt = Counter() #创建一个哈希表
for right, x in enumerate(s):
cnt[x] += 1 #cnt可以这样加1,dic不可以
while cnt[x] > 1: #若是出现重复元素,左端点右移,知道没有重复元素
cnt[s[left]] -= 1 #删掉对左端点的哈希存储
left += 1
result = max(result, right - left + 1)
return result
713、乘积小于k的子数组

解题思路:依然使用两个变量product、num分别表示当前窗口的所有元素乘积以及符合条件的子数组数目。要注意,右边界移动之后乘积若是没有超出k,数组数目不是加1而是加该窗口内所有子数组数目,由于右边界一直在移动更新窗口位置,因此按包含右边界端点往左计算子数组长度。
class Solution:
def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
if k <= 1:
return 0
num = left = 0
product = 1
for right, x in enumerate(nums):
product *= x
while product >= k:
product /= nums[left]
left += 1
# num += 1❌ 注意是所有满足条件的连续子数组个数
# 由于滑动窗口的特性,要计算的是所有包含右端点的子数组
# (r,l)(r+1,l)(r+2,l)...(l,l)总共有l-r+1个
num += right - left + 1
return num
2958、最多k个重复元素的最长子数组

解题思路:这个是滑动窗口和计数器的再一次复习
class Solution:
def maxSubarrayLength(self, nums: List[int], k: int) -> int:
ans = left = 0
cnt = Counter()
for right,x in enumerate(nums):
cnt[x] += 1
while cnt[x]>k:
cnt[nums[left]]-=1
left += 1
ans = max(ans,right-left +1)
return ans
1004、最大连续1的个数3

解题思路:使用计数表示当前翻转0的次数,通过cnt0 +=1-x,若当前元素x是1的话cnt0即为+0,因此并不计数,若当前元素为0.则翻转,此时翻转次数+1,并且比较当前窗口长度和已记录的最大长度并更新。右边界向右直到翻了k个0后,左边界需要更新,注意更新时候要判断左边界是否是被翻的0,若不是(原来就是1)则仍然向后翻直到找到0为止
ps:这道题贼拉难解,写了半天写不出来,看了灵神的题解,感慨一下怎么会有人脑子这么好用,如果看不懂去题解里面再搜一下。
class Solution:
def longestOnes(self, nums: List[int], k: int) -> int:
cnt0=left=ans=0
for right,x in enumerate(nums):
#若是1的话cnt0即为+0,因此会一直往后遍历到翻了k个0后
cnt0 +=1-x
while cnt0>k:
#left向右,若是此位置为0则继续向右翻
cnt0 -= 1-nums[left]
left+=1
ans = max(ans,right-left+1)
return ans
2730、找到最长的半重复子字符串

做题思路:滑动窗口满足条件是t中最有有一对相邻字符。因此右边界移动时若判断和右边界的前一个字符是重复的,就需要记录,并且判断当前窗口内有无其它的相邻重复字符,若是有则需要左边界向左收缩直到窗口内只剩下右边界的重复字符。
class Solution:
def longestSemiRepetitiveSubstring(self, s: str) -> int:
# ans = left = rep_num = 0
# 从1开始比较此位置和前一位置,否则还得判断是否最后一个字符
#ans从1开始!
ans, left, rep_num = 1, 0, 0
for right in range(1, len(s)):
rep_num += s[right] == s[right - 1]
if rep_num >1:
left += 1
while s[left]!= s[left-1]:
left+=1
rep_num = 1
ans = max(ans, right - left + 1)
return ans
2779 数组的最大美丽值


解题思路:
最长子序列的长度,而子序列是不要求是原字符串中的连续子字符串。因此我们只需要找到一个x,字符串中符合[x-k,x+k]这一范围的数组数量就是所谓由相等元素组成的子序列长度(因为在这一范围内的数组都可以替换成x)。
判断数组中符合[x-k,x+k]这一区间的数字最多的范围大小,也是最大“美丽值”。因此我们可以先排序,之后通过滑动窗口,只要右边界仍然小于左边界left+k,就可以持续向右扩展,记录窗口长度。直到超过这一范围,就将左边界右移直到符合右边界right-k范围。
class Solution:
def maximumBeauty(self, nums: List[int], k: int) -> int:
#数组排序
nums.sort()
ans = left = 0
for right, x in enumerate(nums):
while x-nums[left] > k *2:
left +=1
ans = max(ans,right-left+1)
return ans
2962、统计最大元素至少出现k次的子数组
> 首先可以通过max元素直接找到nums的最大元素,之后通过判断当前滑动窗口内的出现了几次最大元素,当窗口内出现最大元素的数量达到k次后,当前窗口就是最小子数组,(left,right)(left,right+1)…(left,n)都是符合条件的数组,因此都可以计入,ans += l-right。之后仍然要判断左端点是否是最大元素,若是的话,在移动左端点前需要把当前窗口最大元素数目-1.(就会进入到下一轮右端点移动找max_num过程),不是的话左端点右移一次继续计数当以当前窗口为最小子数组的数目
class Solution:
def countSubarrays(self, nums: List[int], k: int) -> int:
max_num = max(nums)
l = len(nums)
left = ans = mx = 0
for right,x in enumerate(nums):
if x == max_num:
mx += 1
while mx >=k:
#找到最小窗口后,往后延续的,包括其的所有数组都符合条件
ans += l-right
if nums[left] == max_num:
mx-=1
left+=1
return ans
2302、统计得分小于k的子数组数目

想来做到这里,你已经轻车熟路,这道题对你来说小菜一碟了吧
class Solution:
def countSubarrays(self, nums: List[int], k: int) -> int:
ans = left = 0
sum = 0
l = len(nums)
for right, x in enumerate(nums):
sum +=x
while sum * (right-left+1) >= k :
sum -= nums[left]
left += 1
ans+=right-left+1
return ans
1659、将x减到0的最小操作数

想要不断用数组两边的边界元素使得x减到0。也就是是边界元素相加为x,也就是数组中间片段的和事sum-x。这就是滑动窗口可以解决的问题啦,求减边界元素的最小操作数,也就是求中间片段的最大长度
class Solution:
def minOperations(self, nums: List[int], x: int) -> int:
#滑动窗口找中间最长的片段使得sum(片段)=sum(nums)-x
total = sum(nums)
#就不要再用sum作为变量了!
if total-x<0:
return -1
left = s =0
ans = -1
#一定要注意x,函数传入中有一个x,因此此处不能再设x啦!笨!
for right,y in enumerate(nums):
s += y
while s + x > total:
s -= nums[left]
left += 1
if s + x == total:
ans =max(ans,right-left+1)
return -1 if ans<0 else len(nums)-ans
204

被折叠的 条评论
为什么被折叠?



