目录
3. 无重复字符的最长子串
题目来源:3. 无重复字符的最长子串 - 力扣(LeetCode)
分析:
- 最优解法是使用滑动窗口(双指针)技术。
- 维护一个窗口[left, right],右指针不断扩展窗口,当遇到重复字符时,左指针移动到重复字符的下一个位置。
- 可以使用哈希表记录每个字符最近出现的位置,这样能在O(1)时间内判断是否有重复并快速调整窗口。
- 整个过程中持续更新最大长度值。时间复杂度为O(n),空间复杂度为O(min(m,n)),其中m是字符集大小。
my code:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
res = 0
dic = defaultdict(int)
left = 0
for right,ele in enumerate(s):
dic[ele] += 1
while dic[ele] > 1:
dic[s[left]] -= 1
left += 1
res = max(res, right - left + 1)
return res
注意:
- 非定长滑动窗口中,窗口的移动需要条件约束,当满足这个条件才能移动
- 同时要及时更新结果
- 非定长window中,移除的操作通常需要使用while,这样才能涵盖诸多情况的子序列组合
3090. 每个字符最多出现两次的最长子字符串
题目来源:3090. 每个字符最多出现两次的最长子字符串 - 力扣(LeetCode)
分析:该题与无重复字符最长子串逻辑相同,只需要把1改为2即可
解:
class Solution:
def maximumLengthSubstring(self, s: str) -> int:
res = 0
left = 0
dic = defaultdict(int)
for right,c in enumerate(s):
dic[c] += 1
while dic[c] > 2:
dic[s[left]] -= 1
left += 1
res = max(res,right + 1 - left)
return res
1493. 删掉一个元素以后全为 1 的最长子数组
题目来源:1493. 删掉一个元素以后全为 1 的最长子数组 - 力扣(LeetCode)
分析:
- 因为要求删掉一个元素后的子数组最大长度,所以窗口内允许存在至多一个0(非1元素)
- 可以使用字典来存储0元素个数
- 如果0元素个数大于 1 ,那么滑动窗口直至窗口内0元素数量等于1
- 子数组长度应该 right - left 而不再 +1 因为删掉了其中一个元素0
我的答案:
class Solution:
def longestSubarray(self, nums: List[int]) -> int:
res = left = 0
dic = defaultdict(int)
for right,element in enumerate(nums):
if element != 1:
dic[element] += 1
while dic[element] > 1:
dic[nums[left]] -= 1
left += 1
res = max(res,right - left)
return res
标准题解:
class Solution:
def longestSubarray(self, nums: List[int]) -> int:
ans = cnt0 = left = 0
for right, x in enumerate(nums):
# 1. 入,nums[right] 进入窗口
cnt0 += 1 - x # 维护窗口中的 0 的个数
while cnt0 > 1: # 不符合题目要求
# 2. 出,nums[left] 离开窗口
cnt0 -= 1 - nums[left] # 维护窗口中的 0 的个数
left += 1
# 3. 更新答案,注意不是 right-left+1,因为我们要删掉一个数
ans = max(ans, right - left)
return ans
1208. 尽可能使字符串相等
题目链接:1208. 尽可能使字符串相等 - 力扣(LeetCode)
分析:
- 内置函数可以将字符转换为对应的ascall
- abs内置函数是去绝对值
- 总开销是否大于maxCost作为移动窗口的条件
解答:
class Solution:
def equalSubstring(self, s: str, t: str, maxCost: int) -> int:
res = left = 0
ans = 0 # 记录开销
for right,(s_c,t_c) in enumerate(zip(s,t)):
# 当前位需要的开销
ans_tmp = abs(ord(s_c) - ord(t_c))
# 目前总开销
ans += ans_tmp
while ans > maxCost:
# 开始移除做边
ans -= abs(ord(s[left]) - ord(t[left]))
left += 1
res = max(res,right + 1 - left)
return res
904. 水果成篮
分析:
- 使用字典,哈希表来存储已经装的水果以及对应个数
- 字典长度表示水果种类,不能大于2
- 字典value代表水果个数,其实当前水果个数等于 right - left + 1
- 窗口左端点滑动的条件是哈希表长度大于2,然后不断进行右移,直到长度等于2
- 左端点移动要先不断减少某种水果数量,当数量等于0时删除,否则哈希表长度不变
我的解答,第一次一遍过:
class Solution:
def totalFruit(self, fruits: List[int]) -> int:
dic = defaultdict(int)
res = left = 0
for right,fru in enumerate(fruits):
dic[fru] += 1
while len(dic) > 2:
dic[fruits[left]] -= 1
if dic[fruits[left]] == 0:
del dic[fruits[left]]
left += 1
res = max(res,right +1 - left)
return res
1695. 删除子数组的最大得分
1695. 删除子数组的最大得分 - 力扣(LeetCode)
分析:
- 删除的元素中必须是不同的,而且还得是原数组的子数组
- 所以通过set集合直接求和肯定是不行的
- 使用哈希表存储已经删除的元素,如果元素大于1,那么就说明有删除了重复的元素,需要缩小窗口
- 扩展以及缩小窗口的同时不断的记录删除元素的和
我的解,也是一次过:
class Solution:
def maximumUniqueSubarray(self, nums: List[int]) -> int:
res = left = 0
dic = defaultdict(int)
res_curr = 0
for right,num in enumerate(nums):
dic[num] += 1
res_curr += num
while dic[num] > 1:
dic[nums[left]] -= 1
res_curr -= nums[left]
left += 1
res = max(res,res_curr)
return res
2958. 最多 K 个重复元素的最长子数组
2958. 最多 K 个重复元素的最长子数组 - 力扣(LeetCode)
分析:
- 该题与前两个题目类似,均可以使用模版套用,仅仅需要改变窗口缩小的条件
- 注意及时更新结果
解,一次过:
class Solution:
def maxSubarrayLength(self, nums: List[int], k: int) -> int:
dic = defaultdict(int)
res = 0
left = 0
for right,num in enumerate(nums):
dic[num] += 1
while dic[num] > k:
dic[nums[left]] -= 1
left += 1
res = max(res,right + 1 - left)
return res
2024. 考试的最大困扰度
分析:
- 最终得到的最大连续子串,可能是全为T也可以是F
- 所以我设置了两个窗口,分别是
- 将F替换为T记录不超过k次替换的最长子串
- 将T替换为F记录不超过k次替换的最长子串
- 最后从两个窗口记录的结果中max()较大的一个即可
解答,一次过:
class Solution:
def maxConsecutiveAnswers(self, answerKey: str, k: int) -> int:
dic_f = defaultdict(int)
dic_t = defaultdict(int)
tk = 0
fk = 0
left_t = 0
left_f = 0
for right,ans in enumerate(answerKey):
dic_f[ans] += 1
dic_t[ans] += 1
# 仅仅需要维持窗口内至多有k个F或k个T即可
# T
while dic_f['F'] > k:
dic_f[answerKey[left_t]] -= 1
left_t += 1
tk = max(tk,right + 1 - left_t)
# F
while dic_t['T'] > k:
dic_t[answerKey[left_f]] -= 1
left_f += 1
fk = max(fk,right + 1 - left_f)
return max(tk,fk)
题解:
class Solution:
def maxConsecutiveAnswers(self, answerKey: str, k: int) -> int:
ans = left = 0
cnt = defaultdict(int)
for right, ch in enumerate(answerKey):
cnt[ch] += 1
while cnt['T'] > k and cnt['F'] > k:
cnt[answerKey[left]] -= 1
left += 1
ans = max(ans, right - left + 1)
return ans
总结:
- while cnt['T'] > k and cnt['F'] > k: 可以完全将我的解进行合并
- 因为至多只能修改k个,所以仅仅当t和f的数量同时大于k的时候才需要缩小窗口