非定长滑动窗口(持续更新)

目录

3. 无重复字符的最长子串 

3090. 每个字符最多出现两次的最长子字符串

1493. 删掉一个元素以后全为 1 的最长子数组

1208. 尽可能使字符串相等

904. 水果成篮

 1695. 删除子数组的最大得分


3. 无重复字符的最长子串 

题目来源:3. 无重复字符的最长子串 - 力扣(LeetCode)

分析:

  1.  最优解法是使用滑动窗口(双指针)技术。
  2. 维护一个窗口[left, right],右指针不断扩展窗口,当遇到重复字符时,左指针移动到重复字符的下一个位置。
  3. 可以使用哈希表记录每个字符最近出现的位置,这样能在O(1)时间内判断是否有重复并快速调整窗口。
  4. 整个过程中持续更新最大长度值。时间复杂度为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

注意:

  1. 非定长滑动窗口中,窗口的移动需要条件约束,当满足这个条件才能移动
  2. 同时要及时更新结果
  3. 非定长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)

分析:

  1. 因为要求删掉一个元素后的子数组最大长度,所以窗口内允许存在至多一个0(非1元素)
  2. 可以使用字典来存储0元素个数
  3. 如果0元素个数大于 1 ,那么滑动窗口直至窗口内0元素数量等于1
  4. 子数组长度应该 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)

分析:

  1. 内置函数可以将字符转换为对应的ascall
  2. abs内置函数是去绝对值
  3. 总开销是否大于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. 水果成篮

题目链接:904. 水果成篮 - 力扣(LeetCode)

分析:

  1. 使用字典,哈希表来存储已经装的水果以及对应个数
  2. 字典长度表示水果种类,不能大于2
  3. 字典value代表水果个数,其实当前水果个数等于 right - left + 1
  4. 窗口左端点滑动的条件是哈希表长度大于2,然后不断进行右移,直到长度等于2
  5. 左端点移动要先不断减少某种水果数量,当数量等于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)

 分析:

  1. 删除的元素中必须是不同的,而且还得是原数组的子数组
  2. 所以通过set集合直接求和肯定是不行的
  3. 使用哈希表存储已经删除的元素,如果元素大于1,那么就说明有删除了重复的元素,需要缩小窗口
  4. 扩展以及缩小窗口的同时不断的记录删除元素的和

我的解,也是一次过:

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)

分析:

  1. 该题与前两个题目类似,均可以使用模版套用,仅仅需要改变窗口缩小的条件
  2. 注意及时更新结果 

解,一次过: 

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. 考试的最大困扰度

2024. 考试的最大困扰度 - 力扣(LeetCode)

分析:

  1. 最终得到的最大连续子串,可能是全为T也可以是F
  2. 所以我设置了两个窗口,分别是
    1. 将F替换为T记录不超过k次替换的最长子串
    2. 将T替换为F记录不超过k次替换的最长子串
  3. 最后从两个窗口记录的结果中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

总结:

  1. while cnt['T'] > k and cnt['F'] > k: 可以完全将我的解进行合并
  2. 因为至多只能修改k个,所以仅仅当t和f的数量同时大于k的时候才需要缩小窗口
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值