每日一题,每日一练28.统计[优美子数组](有人相爱,有人半夜看海,有人早上八点写不出来)

统计「优美子数组」 给你一个整数数组 nums 和一个整数 k。
如果某个 连续 子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」。

请返回这个数组中「优美子数组」的数目。

示例 1:

输入:nums = [1,1,2,1,1], k = 3 输出:2
解释:包含 3 个奇数的子数组是 [1,1,2,1] 和
[1,2,1,1] 。

示例 2:

输入:nums = [2,4,6], k = 1 输出:0 解释:数列中不包含任何奇数,所以不存在优美子数组。 示例 3:

输入:nums = [2,2,2,1,2,2,1,2,2,2], k = 2 输出:16

写了两个小时的滑动窗口,改了无数次条件,终于意识到了这不是仅仅要改窗口大小的事,被迫滚去看题解。然后又花了一个小时弄懂题解(我觉得各个学科都是不用专业名词很好懂)。简单解释一下。
由于数组中只有奇数有用,我们可以设立一个数组,用来记录第i个奇数前面带了几个偶数(不是奇数就是偶数)
比如[212221],第一个奇数前面带了一个偶数,第二个带了三个,所以数组是[1,3]

如果奇数不到k个,显然没有子数组,那么从第k个奇数开始,将每个奇数作为子数组的终点,因为子数组要有k个奇数,所以子数组第一个数如果是奇数必须是第一个奇数,这是极限状态,然后,我们发现这个子数组还可以在前面塞偶数,就是我们第一个奇数在数组的记录值。于是我们这样递推下去,对第i个奇数,可以组成的子数组都是极限状态+第i-k+1个奇数携带的偶数(可以塞在数组最前面

那么什么时候可以塞在最后面呢,这里我们还是基于极限状态来考虑。很显然,只有偶数能塞在极限状态的后面,如果我们塞进去一个奇数,就要修改极限状态

那么,当极限状态已经形成(统计已经到k个奇数时),如果遍历再获得奇数,我们当然记录到奇数数组并确立新的极限状态和状况**,如果是偶数,我们就可以插在之前极限状态的后面,这时候要注意,新增的符合条件的数组并不是加1,由于极限状态的前插和后插不互相干涉,我们可以认为之前的每种前插状况都可以和后插组成符合条件**

于是,我们的思路流程如下

记录每个奇数前面携带的偶数

从第i(i>=k)个奇数开始,把这个奇数作为子数组终点

由于子数组要有k个奇数,找到符合条件的第一个奇数(i-k)

确立子数组的极限状态,也就是两边都为奇数且奇数为k,不能再减少了

第一个奇数在前面能插入多少偶数,就有多少种满足条件的子数组

在存在极限状态后,分支如下:
如果接下来记录奇数,执行原思路流程

**如果接下来是偶数,叠加一次最近的极限状态满足条件的子数组,比如最近的奇数所确立的极限状态里第一个奇数前面携带4个偶数,则有极限状态+4种前插总计5个子数组,那么每后插一个偶数则咋满足条件的数目ans=ans+5

代码如下(官方的代码,因为再手撕好像变不出来什么新的花样了==0)

class Solution:
    def numberOfSubarrays(self, nums: List[int], k: int) -> int:
        cnt = [0] * (len(nums) + 1)
        cnt[0] =1
        odd, ans = 0, 0
        for num in nums:
            if num % 2 == 1:
                odd += 1
            if odd >= k:
                ans += cnt[odd - k]#
            cnt[odd] += 1#
        return ans
#作者:LeetCode-Solution
#链接:https://leetcode-cn.com/problems/count-number-of-nice-#subarrays/solution/tong-ji-you-mei-zi-shu-zu-by-leetcode-solution/
#来源:力扣(LeetCode)
#著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

注释太黑了我们按行解释一下

cnt = [0] * (len(nums) + 1) ### 创建奇数携带记录数组

cnt[0] =1 ##这里是给第一个奇数本身先占一个位置,也就是我们的极限状态

odd, ans = 0, 0 ## odd代表当前记录第几个奇数,ans代表答案

for num in nums: ##开始遍历原数组

if num % 2 == 1:#如果是奇数,意味着这个奇数已经被记录完成,因为这个奇数本身我们占过位了

odd += 1 #我们开始专注应对下一个奇数

if odd >= k:#如果已经有极限状态存在

ans += cnt[odd - k]#如果是奇数,我们将新的奇数当成子数组最后一个奇数,确立新的极限状态和可以前插的数目(odd位移过了),如果是偶数,我们叠加最新的极限状态可以前插的数目(理由如上)(odd没有位移

cnt[odd] += 1# 如果是奇数,这里为这个奇数占位,如果是偶数,增加这个奇数的携带偶数一个

return ans#循环整个数组后返回答案

现在再回去看看代码,是否清楚一点呢

有人相爱,有人半夜看海,有人早上八点写不出来

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值