本质:可用于解决一些子串,子数组的问题,r指针一直往右走,l指针按条件往右走
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:如果 S 中不存这样的子串,则返回空字符串 ""。
如果 S 中存在这样的子串,我们保证它是唯一的答案。1.构建一个滑动窗口,左右指针怎么移动?右指针移动:当窗口内的内容不满足条件 左指针:当窗口内的指针满足条件
class Solution:
def minWindow(self, s: 'str', t: 'str') -> 'str':
t=collections.Counter(t)
lookup=collections.defaultdict(int)
minLen=float('inf')
res=""
l=r=0
while r<len(s):
lookup[s[r]]+=1
r+=1
while all(map(lambda x:lookup[x]>=t[x],t.keys())):
if r-l<minLen:
res=s[l:r]
minLen=r-l
lookup[s[l]]-=1
l+=1
return res
class Solution:
def minWindow(self, s: 'str', t: 'str') -> 'str':
lookup=collections.defaultdict(int)
for i in t:lookup[i]+=1
count=len(t)
l=r=0
min_len=float('inf')
res=""
while r<len(s):
if lookup[s[r]]>0:count-=1
lookup[s[r]]-=1
r+=1
while count==0:
if min_len>r-l:
min_len=r-l
res=s[l:r]
if lookup[s[l]]==0:count+=1
lookup[s[l]]+=1
l+=1
return res
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。1.滑动+hash,hash用于存储内容,用于判断当前窗口内是否出现了重复
class Solution:
def lengthOfLongestSubstring(self, s):
lookup=collections.defaultdict(int)
res=0
l=r=0
count=0
while r<len(s):
if lookup[s[r]]>0:count+=1#这个字符前面出现过
lookup[s[r]]+=1
r+=1
while count>0:
if lookup[s[l]]>1:count-=1
lookup[s[l]]-=1
l+=1
res=max(res,r-l)
return res
class Solution(object):
def lengthOfLongestSubstring(self, s):
if not s:return 0
l=r=0
res=0
h={}
while r<len(s):
if s[r] in h:
l=max(l,h[s[r]])
res=max(res,r-l+1)
h[s[r]]=r+1
r+=1
return res
给定一个由若干 0 和 1 组成的数组 A,我们最多可以将 K 个值从 0 变成 1 。
返回仅包含 1 的最长(连续)子数组的长度。
示例 1:
输入:A = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:
[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。1.直接暴力o(n³)
2.sum优化成前缀和暴力o(n²)
3.滑动窗口,右指针一直右走,左指针当窗口内的0的个数>k时右走
class Solution:
def longestOnes(self, A: List[int], K: int) -> int:
if K>=A.count(0):return len(A)
res=0
for i in range(len(A)):
for j in range(i+1,len(A)):
if j-i+1-sum(A[i:j+1])==K:
res=max(res,sum(A[i:j+1])+K)
return res
class Solution:
def longestOnes(self, A: List[int], K: int) -> int:
if K>=A.count(0):return len(A)
res=0
pre=[0 for _ in range(len(A))]
pre[0]=A[0]
for i in range(1,len(A)):
pre[i]=pre[i-1]+A[i]
for i in range(len(A)):
for j in range(i+1,len(A)):
if i-1>=0:sum_=pre[j]-pre[i-1]
else:sum_=pre[j]
if j-i+1-sum_==K:
res=max(res,sum_+K)
return res
class Solution:
def longestOnes(self, A: List[int], K: int) -> int:
if K>=len(A):return len(A)
l=r=0
res=0
count=0
while r<len(A):
if A[r]==0:count+=1
while count>K:
if A[l]==0:count-=1
l+=1
res=max(res,r-l+1)
r+=1
return res
今天,书店老板有一家店打算试营业 customers.length 分钟。每分钟都有一些顾客(customers[i])会进入书店,所有这些顾客都会在那一分钟结束后离开。
在某些时候,书店老板会生气。 如果书店老板在第 i 分钟生气,那么 grumpy[i] = 1,否则 grumpy[i] = 0。 当书店老板生气时,那一分钟的顾客就会不满意,不生气则他们是满意的。
书店老板知道一个秘密技巧,能抑制自己的情绪,可以让自己连续 X 分钟不生气,但却只能使用一次。
请你返回这一天营业下来,最多有多少客户能够感到满意的数量。
1.暴力直接枚举grumpy数组连续的x个数为0的情况
2.滑动窗口
class Solution:
def maxSatisfied(self, customers: List[int], grumpy: List[int], X: int) -> int:
res=0
def func(index):
ans=0
for i in range(len(customers)):
if grumpy[i]==0 or index<=i<=index+X-1:
ans+=customers[i]
return ans
for i in range(len(grumpy)-X+1):
res=max(res,func(i))
return res
class Solution:
def maxSatisfied(self, customers: List[int], grumpy: List[int], X: int) -> int:
res1=0
for i in range(len(customers)):
if grumpy[i]==0:
res1+=customers[i]
customers[i]=0
l=0
r=X-1
tmp_sum=sum(customers[:X])
res2=tmp_sum
while r<len(customers):
r+=1
if r>len(customers)-1:break
tmp_sum+=customers[r]
tmp_sum-=customers[l]
l+=1
res2=max(res2,tmp_sum)
return res1+res2
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
示例 1:
输入:
["MaxQueue","push_back","push_back","max_value","pop_front","max_value"]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]1.用普通队列实现,max_value用暴力获取其值
2.用一个辅助的单调队列,单调队列的队首始终保持着队列的最大值
class MaxQueue:
def __init__(self):
self.q=collections.deque()
def max_value(self) -> int:
return max(self.q) if self.q else -1
def push_back(self, value: int) -> None:
self.q.append(value)
def pop_front(self) -> int:
return self.q.popleft() if self.q else -1
class MaxQueue(object):
def __init__(self):
self.q=collections.deque()
self.q_keep=collections.deque()
def max_value(self):
return self.q_keep[0] if self.q_keep else -1
def push_back(self, value):
self.q.append(value)
while self.q_keep and self.q_keep[-1]<value:
self.q_keep.pop()
self.q_keep.append(value)
def pop_front(self):
if not self.q:return -1
res=self.q.popleft()
if res==self.q_keep[0]:self.q_keep.popleft()
return res
给你一个整数数组 nums ,和一个表示限制的整数 limit,请你返回最长连续子数组的长度,该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit 。
如果不存在满足条件的子数组,则返回 0 。
1.很好的题,滑动窗口(一般求子串,子区间问题可以考虑),滑动窗口问题需要考虑2个指针怎么移动,r指针可以走的话一直往右走,左指针当窗口内容不满足条件时往左走,这里的条件就是窗口内的max_value-min_value<=limit,这里就涉及到另一个知识点,怎么用o(1)的时间获得当前区间的最值?单调队列!,维护2个单调队列!
class Solution:
def longestSubarray(self, nums: List[int], limit: int) -> int:
q_max=[]
q_min=[]
res=0
l=r=0
while r<len(nums):
while q_max and nums[r]>nums[q_max[-1]]:
q_max.pop()
q_max.append(r)
while q_min and nums[r]<nums[q_min[-1]]:
q_min.pop()
q_min.append(r)
while q_min and q_max and nums[q_max[0]]-nums[q_min[0]]>limit:
if q_max[0]<=l:q_max.pop(0)
if q_min[0]<=l:q_min.pop(0)
l+=1
res=max(res,r-l+1)
r+=1
return res
几张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。
每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k 张卡牌。
你的点数就是你拿到手中的所有卡牌的点数之和。
给你一个整数数组 cardPoints 和整数 k,请你返回可以获得的最大点数。
输入:cardPoints = [1,2,3,4,5,6,1], k = 3
输出:12
解释:第一次行动,不管拿哪张牌,你的点数总是 1 。但是,先拿最右边的卡牌将会最大化你的可获得点数。最优策略是拿右边的三张牌,最终点数为 1 + 6 + 5 = 12 。1.贪心+前缀和
思考?
要怎么才能判断拿左边还是右边呢,想一下,只能拿3张,怎么判断先拿左还是右,不能只看一个数,要看当前可以拿的牌数来决定,对于3张而言就是要一次考虑左右拿3张才能决定是先拿左还是右!
2.dfs直接左右2种情况搜索,超时
3.滑动窗口
分析:
看最后的效果,起始就是一直在左右拿牌,最后剩下中间的连续子数组,所以求中间子数组的最小和即可!
class Solution:
def maxScore(self, cardPoints: List[int], k: int) -> int:
n=len(cardPoints)
left=[0 for _ in range(n+1)]
right=[0 for _ in range(n+1)]
for i in range(n):
left[i+1]=left[i]+cardPoints[i]
right[i+1]=right[i]+cardPoints[n-i-1]
res=0
l=r=0
u=k
for i in range(k):
if left[l+u]-left[l]>right[r+u]-right[r]:
res+=cardPoints[l]
l+=1
else:
res+=cardPoints[n-r-1]
r+=1
u-=1
return res
class Solution:
def maxScore(self, cardPoints: List[int], k: int) -> int:
if k==len(cardPoints):return sum(cardPoints)
def dfs(l,r,k):
if k==1:return max(cardPoints[l],cardPoints[r])
return max(dfs(l+1,r,k-1)+cardPoints[l],dfs(l,r-1,k-1)+cardPoints[r])
return dfs(0,len(cardPoints)-1,k)
class Solution:
def maxScore(self, cardPoints: List[int], k: int) -> int:
if len(cardPoints) == k:
return sum(cardPoints)
res_opposite = sum(cardPoints[:len(cardPoints) - k])
# 变量存储滑窗的最小值,这里先按初始位置赋初值
current_sum = res_opposite # current_sum记录当前滑窗总和
left_idx, right_idx = 0, len(cardPoints) - k - 1
for _ in range(k): # 滑窗将移动 k 次
left_idx += 1
right_idx += 1
current_sum = current_sum - cardPoints[left_idx - 1] + cardPoints[right_idx]
res_opposite = min(res_opposite, current_sum)
return sum(cardPoints) - res_opposite
1477.给你一个整数数组 arr 和一个整数值 target 。
请你在 arr 中找 两个互不重叠的子数组 且它们的和都等于 target 。可能会有多种方案,请你返回满足要求的两个子数组长度和的 最小值 。
请返回满足要求的最小长度和,如果无法找到这样的两个子数组,请返回 -1 。
1.滑窗生成所有结果,根据长度排序,最后选出不相交
class Solution:
def minSumOfLengths(self, arr: List[int], target: int) -> int:
tmp=[]
hash=[]
l=r=0
tmp_sum=0
for r in range(len(arr)):
tmp_sum+=arr[r]
while s>target:
s-=arr[l]
l+=1
if s==target:
tmp.append((l,r,r-l+1))
tmp.sort(key=lambda x:x[2])
if len(tmp) < 2:
return -1
else:
t = tmp[0]
for i in range(1,len(tmp)):
if tmp[i][0]>t[1] or tmp[i][1]<t[0]:
return tmp[i][2]+t[2]
return -1
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。1.滑动窗口+hash
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
lookup=set()
left=right=0
res=0
while right<len(s):
while s[right] in lookup:
lookup.remove(s[left])
left+=1
lookup.add(s[right])
right+=1
res=max(res,right-left)
return res
在一个由小写字母构成的字符串 S 中,包含由一些连续的相同字符所构成的分组。
例如,在字符串 S = "abbxxxxzyy" 中,就含有 "a", "bb", "xxxx", "z" 和 "yy" 这样的一些分组。
我们称所有包含大于或等于三个连续字符的分组为较大分组。找到每一个较大分组的起始和终止位置。
最终结果按照字典顺序输出。
示例 1:
输入: "abbxxxxzzy"
输出: [[3,6]]
解释: "xxxx" 是一个起始于 3 且终止于 6 的较大分组。1.暴力
2.典型的滑动窗口
class Solution:
def largeGroupPositions(self, S: str) -> List[List[int]]:
res=[]
t=0
for i in range(len(S)):
if i!=0 and i<=t:continue
tmp=1
for j in range(i+1,len(S)):
if S[j]==S[i]:tmp+=1
else:break
if tmp>=3:
t=i+tmp-1
res.append([i,i+tmp-1])
return res
class Solution:
def largeGroupPositions(self, S: str) -> List[List[int]]:
res=[]
left=right=0
while right<len(S):
right+=1
while right<len(S) and S[right]==S[left]:right+=1
if right-1-left>=2:res.append([left,right-1])
left=right
return res
假设你有两个数组,一个长一个短,短的元素均不相同。找到长数组中包含短数组所有的元素的最短子数组,其出现顺序无关紧要。
返回最短子数组的左端点和右端点,如有多个满足条件的子数组,返回左端点最小的一个。若不存在,返回空数组。
示例 1:
输入:
big = [7,5,9,0,2,1,3,5,7,9,1,1,5,8,8,9,7]
small = [1,5,9]
输出: [7,10]示例 2:
输入:
big = [1,2,3]
small = [4]
输出: []提示:
big.length <= 100000
1 <= small.length <= 1000001.滑动窗口+窗口内hash计数
class Solution:
def shortestSeq(self, big: List[int], small: List[int]) -> List[int]:
t=Counter(small)
left=right=0
res=float('inf')
l=r=float('inf')
lookup=defaultdict(int)
while right<len(big):
lookup[big[right]]+=1
while all(map(lambda x:lookup[x]>=t[x],t.keys())):
if right-left+1<res or (right-left+1==res and left<l):
res=min(res,right-left+1)
l=left
r=right
lookup[big[left]]-=1
left+=1
right+=1
return [l,r] if l!=float('inf') and r!=float('inf') else []
给你两个长度相同的字符串,s 和 t。
将 s 中的第 i 个字符变到 t 中的第 i 个字符需要 |s[i] - t[i]| 的开销(开销可能为 0),也就是两个字符的 ASCII 码值的差的绝对值。
用于变更字符串的最大预算是 maxCost。在转化字符串时,总开销应当小于等于该预算,这也意味着字符串的转化可能是不完全的。
如果你可以将 s 的子字符串转化为它在 t 中对应的子字符串,则返回可以转化的最大长度。
如果 s 中没有子字符串可以转化成 t 中对应的子字符串,则返回 0。
示例 1:
输入:s = "abcd", t = "bcdf", cost = 3
输出:3
解释:s 中的 "abc" 可以变为 "bcd"。开销为 3,所以最大长度为 3。
class Solution:
def equalSubstring(self, s: str, t: str, maxCost: int) -> int:
n=len(s)
tmp=[0 for _ in range(n)]
for i in range(n):
tmp[i]=abs(ord(s[i])-ord(t[i]))
if maxCost<min(tmp):return 0
left=right=0
s=0
res=0
while right<n:
s+=tmp[right]
while s>maxCost and left<right:
s-=tmp[left]
left+=1
res=max(res,right-left+1)
right+=1
return res
给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。
如果可以将 x 恰好 减到 0 ,返回 最小操作数 ;否则,返回 -1 。
示例 1:
输入:nums = [1,1,4,2,3], x = 5
输出:2
解释:最佳解决方案是移除后两个元素,将 x 减到 0 。1.滑动窗口 ①求2边 ②求中间
class Solution:
def minOperations(self, nums: List[int], x: int) -> int:
if sum(nums)<x:return -1
n=len(nums)
nums=nums+nums
check=[0,n-1,n,2*n-1]
left=right=0
s=0
res=float('inf')
while right<2*n:
s+=nums[right]
while s>x and left<right:
s-=nums[left]
left+=1
if s==x:
flag=False
for c in check:
if left<=c<=right:
flag=True
if flag:
res=min(res,right-left+1)
right+=1
return res if res!=float('inf') else -1
class Solution:
def minOperations(self, nums: List[int], x: int) -> int:
if sum(nums) == x:return len(nums)
n=len(nums)
target=sum(nums)-x
res=float('inf')
left=right=0
s=0
while right<n:
s+=nums[right]
while s>target and left<right:
s-=nums[left]
left+=1
if s==target:
res=min(res,n - (right-left+1))
right+=1
return res if res!=float('inf') else -1
给你一个整数数组 nums ,和一个表示限制的整数 limit,请你返回最长连续子数组的长度,该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit 。
如果不存在满足条件的子数组,则返回 0 。
示例 1:
输入:nums = [8,2,4,7], limit = 4
输出:2
解释:所有子数组如下:
[8] 最大绝对差 |8-8| = 0 <= 4.
[8,2] 最大绝对差 |8-2| = 6 > 4.
[8,2,4] 最大绝对差 |8-2| = 6 > 4.
[8,2,4,7] 最大绝对差 |8-2| = 6 > 4.
[2] 最大绝对差 |2-2| = 0 <= 4.
[2,4] 最大绝对差 |2-4| = 2 <= 4.
[2,4,7] 最大绝对差 |2-7| = 5 > 4.
[4] 最大绝对差 |4-4| = 0 <= 4.
[4,7] 最大绝对差 |4-7| = 3 <= 4.
[7] 最大绝对差 |7-7| = 0 <= 4.
因此,满足题意的最长子数组的长度为 2 。
示例 2:输入:nums = [10,1,2,4,7,2], limit =
1.2个单调队列获取最大和最小值,然后滑动窗口
class Solution:
def longestSubarray(self, nums: List[int], limit: int) -> int:
q_max=[]
q_min=[]
res=0
left=0
right=0
while right<len(nums):
while q_max and nums[right]>nums[q_max[-1]]:q_max.pop()
while q_min and nums[right]<nums[q_min[-1]]:q_min.pop()
q_max.append(right)
q_min.append(right)
while abs(nums[q_max[0]]-nums[q_min[0]])>limit:
if q_max[0]<=left:q_max.pop(0)
if q_min[0]<=left:q_min.pop(0)
left+=1
res=max(res,right-left+1)
right+=1
return res