文章目录
前言
整理力扣刷题思路。
- 语言:python
- 题库:来自neetcode: link
一、预备知识
滑动窗口法
滑动窗口法是一种常用的算法策略,主要用于解决数组/列表中的子元素问题,如求取最大子序列和、最小子序列长度等问题。其主要思想是维护一个窗口,通过移动窗口来遍历整个数组/列表,从而找到问题的解。
滑动窗口法的基本步骤如下:
- 初始化一个窗口。窗口的大小和位置根据具体问题来定。
- 移动窗口并更新窗口内的信息。通常,窗口的移动是通过改变窗口的起始和结束位置来实现的。窗口内的信息可能包括窗口内元素的和、最大值、最小值等。
- 根据窗口内的信息来求解问题。例如,如果问题是求最大子序列和,那么就需要在每次移动窗口后,比较当前的子序列和与之前的最大子序列和。
滑动窗口法的优点是可以在线性时间复杂度内解决一些看似复杂的问题,因为每个元素只需要被访问一次或两次。但是,滑动窗口法也有其局限性,它并不适用于所有的子元素问题,只有当问题满足一定的性质(如最大子序列和问题的最优子结构性质)时,才能使用滑动窗口法。
二、解题思路
1.窗口长度不固定
3.longest-substring-without-repeating-characters
给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:
return 0
lookup = set()
start = 0
cnt = 0 #当前窗口长度
mx = 0 #最大窗口长度
for i in range(len(s)):
#出现重复数时更新最大长度
#从左端一直删除至重复的那个数
if s[i] in lookup:
mx = max(mx,cnt)
while s[i] in lookup:
lookup.remove(s[start])
start += 1
cnt -= 1
lookup.add(s[i])
cnt += 1
return max(mx,cnt)
问题图解可以参考链接: link
424.longest-repeating-character-replacement
给你一个字符串 s 和一个整数 k 。你可以选择字符串中的任一字符,并将其更改为任何其他大写英文字符。该操作最多可执行 k 次。
在执行上述操作后,返回 包含相同字母的最长子字符串的长度。
class Solution:
def characterReplacement(self, s: str, k: int) -> int:
n = len(s)
if n < 2:
return n
l,r = 0,0
max_cnt = 0 #当前窗口内出现次数最多的字母的计数
hashtable = {}
while r < n:
hashtable[s[r]] = 1 + hashtable.get(s[r],0)
max_cnt = max(max_cnt, hashtable[s[r]])
#要替换的字符数大于k时,窗口左边界右移
if r-l+1-max_cnt > k:
hashtable[s[l]] -= 1
l += 1
r += 1
return r-l
76.minimum-window-substring
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
class Solution:
def minWindow(self, s: str, t: str) -> str:
m,n = len(s),len(t)
if not t or m<n:
return ''
#这样定义使得访问不存在的key时会返回0,而不是报错
hashtable = collections.defaultdict(int)
for i in range(n):
hashtable[t[i]] += 1
l,cnt = 0,n
window = (0,float('inf'))
for r in range(m):
if hashtable[s[r]] > 0:
cnt -= 1
hashtable[s[r]] -= 1
#当前窗口包含了t的所有元素
if cnt==0:
#不断右移l,移除窗口中的非t元素
while l<r:
if hashtable[s[l]] == 0:
break
hashtable[s[l]] += 1
l += 1
#记录当前窗口长度及左右端
if r-l+1 < window[1]-window[0]:
window = (l,r+1)
hashtable[s[l]] += 1
cnt += 1
l += 1
if window[1] > m:
return ''
else:
return s[window[0]:window[1]]
这题是hard难度,解题思路参考:链接: link
2.窗口长度固定
567.permutation-in-string
给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。
换句话说,s1 的排列之一是 s2 的 子串 。
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
if not s1:
return True
m,n = len(s1),len(s2)
if m>n:
return False
hash1,hash2 = [0]*26, [0]*26
for i in range(m):
hash1[ord(s1[i]) - ord('a')] += 1
hash2[ord(s2[i]) - ord('a')] += 1
for j in range(n-m):
if hash1==hash2:
return True
else:
# 窗口整体右移一位
hash2[ord(s2[j]) - ord('a')] -= 1
hash2[ord(s2[j+m]) - ord('a')] += 1
return hash1==hash2
239.sliding-window-maximum
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
if not nums or k==0:
return []
window = [] #队列用于存储最多一个窗口长度的数组
#未形成窗口时
for i in range(k):
#保证降序排列
while window and nums[i] > window[-1]:
window.pop()
window.append(nums[i])
#最大值数组,加入第一个窗口内最大值
ans = [window[0]]
#已形成窗口后,遍历到最后
for i in range(k,len(nums)):
#若窗口已满,移除最左边的值
if nums[i-k] == window[0]:
window.pop(0)
while window and nums[i] > window[-1]:
window.pop()
window.append(nums[i])
ans.append(window[0])
return ans
参考链接:链接: link