leetcode滑动窗口;子串最长长度问题 转化为 子串个数问题。leetcode 992 K 个不同整数的子数组;159至多包含两个不同字符的最长子串;904水果成篮;

满足一定条件的最长子串[或者子数组]问题,典型题可以参考leetcode159和904,如下。

leetcode 159至多包含两个不同字符的最长子串;

给定一个字符串 s ,找出 至多 包含两个不同字符的最长子串 t ,并返回该子串的长度。
示例 1:
输入: “eceba”
输出: 3
解释: t 是 “ece”,长度为3。

leetcode 904水果成篮

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。
示例 1:
输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。
示例 2:
输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。
示例 3:
输入:fruits = [1,2,3,2,2]
输出:4
示例 4:
输入:fruits = [3,3,3,1,2,1,1,2,3,3,4]
输出:5
解释:可以采摘 [1,2,1,1,2] 这五棵树。

从求 子串最长长度 到 子串最多个数

上面两道题基本一样 代码如下

class Solution:
    def lengthOfLongestSubstringTwoDistinct(self, s: str) -> int:
        n = len(s)
        char_count={} # key:字符 value:字符个数
        l=0
        res=0
        for r in range(n):
            char_count[s[r]]=char_count.get(s[r],0)+1
            if len(char_count)<=2: #最多只有两种字符 在此情况找最长字符
                res=max(res, r-l+1) 
            while len(char_count)>2:# # l右移直到只有两种字符,同时更新字典
                char_count[s[l]]-=1
                if char_count[s[l]]==0:
                    del char_count[s[l]]
                l+=1
        return res

上面两题核心都是求res=max(res, r-l+1),即最多只包含k个不同整数的子序列最长长度。

如果问 A 中由最多 K 个不同整数组成的子数组的个数,该怎么办呢?
当满足len(char_count)<=k的时候,l到r区间的内,所有子数组都是满足条件的,这些子数组都是都是最多k个不同整数组成。
但是区间是动态变化的,也就是l和r动态变化,我们如果直接数出l到r之间子数组的个数,那这样可能会重复计数。
因此,这里我们考虑用leetcode 467 和 leetcode 795里面的计数思路。
res表示之前满足条件的个数,curr_num满足当前循环内满足条件的个数,具体的,curr_num表示满足条件的,并且以r为结尾的子数组,这样每一轮我们都有
res+=curr_num,也就是只需要求出每一轮的curr_num即可。
所以在上述问题中,求l到r区间内,以r为结尾的,最多k个不同整数组成的子数组个数,数值上就是区间长度r-l+1。
所以res=res+curr_num = res+r-l+1

leetcode 992 K 个不同整数的子数组

给定一个正整数数组 nums和一个整数 k ,返回 num 中 「好子数组」 的数目。
如果 nums 的某个子数组中不同整数的个数恰好为 k,则称 nums 的这个连续、不一定不同的子数组为 「好子数组 」。
例如,[1,2,3,1,2] 中有 3 个不同的整数:1,2,以及 3。
子数组 是数组的 连续 部分。
示例 1:
输入:nums = [1,2,1,2,3], k = 2
输出:7
解释:恰好由 2 个不同整数组成的子数组:[1,2], [2,1], [1,2], [2,3], [1,2,1], [2,1,2], [1,2,1,2].
示例 2:
输入:nums = [1,2,1,3,4], k = 3
输出:3
解释:恰好由 3 个不同整数组成的子数组:[1,2,1,3], [2,1,3], [1,3,4].

class Solution:
    def atMost(self, nums, k):
        #某个子数组中的整数不大于k,求满足条件的子数组的个数
        n=len(nums)
        l,res = 0,0
        val_count={}
        for r in range(n):
            val_count[nums[r]]=val_count.get(nums[r],0)+1
            while len(val_count)>k:
                val_count[nums[l]]-=1
                if val_count[nums[l]]==0:
                    del val_count[nums[l]]
                l+=1
            if len(val_count)<=k: # 注意这里的if需要在while循环后面,因为需要在l变化之后计数
                res+=r-l+1
                # print(res,r,l)
        return res
    def subarraysWithKDistinct(self, nums: List[int], k: int) -> int:
        return self.atMost(nums,k) -self.atMost(nums,k-1)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值