————技巧点:滑动窗口
题目描述:
【中等题】
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
思路分析:
要求:求其和 ≥ s \geq s ≥s的最小连续子数组长度
求解子数组问题时,需要先搞清楚以下几个问题:
连续不连续,如果是连续,有没有元素大小的情况?
题目中是连续的子数组,且不用考虑元素的大小情况。
没有解的情况需不需要考虑?
题目中没有解的情况,默认返回为0。怎么判断没有解,可以在开始设定初始值,最后判断如果初始值不变的话,就返回0.
返回的是什么?是子数组,还是子数组长度?
如果是只返回长度值,就不用考虑多组解的情况。如果是返回最短子数组,就要考虑多组解的情况–如果是要只返回某一个解,那么这个解有什么限定?如果是要返回多个解,那么这多个解按什么顺序来返回?
题解一:暴力法:双层遍历
class Solution:
def minSubArrayLen(self, s: int, nums: List[int]) -> int:
if not nums:
return 0
n = len(nums)
ans = n + 1#初始化最小长度
for i in range(n):
total = 0
for j in range(i, n):
total += nums[j]
if total >= s:
ans = min(ans, j - i + 1)#更新最小长度
break
return 0 if ans == n + 1 else ans
这种方法时间复杂度为 O ( N 2 ) O(N^2) O(N2),时间太长,不建议这种方法,并且python语言实现会出现超出时间限制这一问题。
题解二:滑动窗口法
\quad \quad 在上一题解中,其中包含了大量的重复计算,对于nums[i,j]到nums[i+1,j]的和,只需要减去nums[i]即可,但是刚刚的操作是又通过一遍数组来通过。如何避免这种重复操作呢?其实,从这到那就是一个滑动的过程,将子数组nums[i,j]
定义一个窗口,根据具体情况具体滑动。针对本题,具体过程如下:
初始化窗口:即初始化指针l=0,r=-1
初始累加和total=0
初始最小长度res为其达不到的最大值len+1
移动指针/滑动找到可行解并优化执行以下操作:
和-nums
,左指针向右移动即l+=1,直到条件不再满足(这里是一个循环),也就相当于在可行解的基础上收缩窗口,使其长度更小,如果此时和不满足条件时再扩张窗口即可。如果最小长度还是初始值,则无解,返回0
返回res
class Solution:
def minSubArrayLen(self, s: int, nums: List[int]) -> int:
#[nums[l]...nums[r]]为滑动窗口,初始其无效
l,r=0,-1
#初始累加和为0
total=0
#记录当前寻找到最小长度,因求最小,故初始化为最大值(不可能取到这个值)
res=len(nums)+1
# 滑动窗口的左边界小于len,这是充分条件,这样右边界才可以取值
while l<len(nums):
'''
对于数组问题而言,一旦用方括号来取值的话,一定要注意数组越界的问题:
需要保证r还在取值范围内,因此在条件需要进行限定,因l限定到len-1,故r需小于len-1
'''
if r<len(nums)-1 and total<s:
#扩大窗口
r+=1
total+=nums[r]
else:
total-=nums[l]
l+=1
if total>=s:
res=min(res,r-l+1)
if res==len(nums)+1: #无解
return 0
return res
题目描述:
【中等题】
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!
如何移动?
我们只要把队列的左边的元素移出就行了,直到满足题目要求!
一直维持这样的队列,找出队列出现最长的长度时候,求出解!
【python3代码实现】
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:
return 0
left=0 # 初始化左指针
lookup=set()# 存放已经遍历过的字符
max_len=0
cur_len=0 # 初始化最大长度以及当前长度为0
for i in range(len(s)):
cur_len+=1
while s[i] in lookup:
lookup.remove(s[left])# 移除最左边元素
left+=1
cur_len-=1
if cur_len>max_len:
max_len=cur_len
lookup.add(s[i])
return max_len
题目描述:
【中等题】
给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。
字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
说明:
思路分析:
【python 3 代码实现】
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
res=[] # 存放结果
needs={} # 以字典的形式存储目标字符串中各个字符的数量
windows={} # 记录窗口中各个字符的数量
for c in p:
needs[c]=needs.get(c,0)+1
length=len(p)
limit=len(s)
left=right=0 # 左右指针,代表窗口的左右边界
while right<limit:
c=s[right]
if c not in needs: #当遇到不需要的字符时
windows.clear() # 将之前统计的信息清空
left=right=right+1
else:
windows[c]=windows.get(c,0)+1 #统计目标字符出现的次数
if right-left+1==length: #如果窗口长度等于目标字符串长度,则有可能是字母异位词
if windows==needs: # 如果窗口字典与目标字典完全一样,则是字母异位词
res.append(left) # 将起始索引添加到结果中
windows[s[left]]-=1 # 窗口函数起始索引所对应的元素的计数减一
left+=1 # 左指针向右移一位
right+=1 # 右指针向右移动一位
return res
python dict.get()
题目描述:
【困难题】
给你一个字符串 S、一个字符串 T 。请你设计一种算法,可以在 O(n) 的时间复杂度内,从字符串 S 里面找出:包含 T 所有字符的最小子串。
未完待续 一天一道