LeetCode(1248):统计【优美子数组】

1. 题目描述

如图所示:

2. 题目理解

看到这个“子数组”的概念,首先会想到,如果数组中存在优美子数组,那么会有很多“子数组”都是由一些最基本的“子数组”派生而来的,这些最基本的“子数组”,应该是包含k个奇数,并且元素数目最少的那些数组,不妨记为Si。则从Si可以向前或者向后包含序列中的偶数(如果存在),进而派生出其他的“子数组”。

有了这个思路之后,就明确了基本方向:

  1. 先找数组序列中所有的奇数,记录奇数元素的下标tmp[i],如果找到的奇数个数小于k,直接return 0
  2. 从前到后,找到包含k个奇数并且元素数目最少的子数组,将其作为整体SiSinums[]中下标从tmp[i]tmp[i+k-1]的元素组成;
  3. Si前后加上“前缀”或者“后缀”,可以得到由此子数组派生出的所有“优美子数组”。
  4. 遍历tmp[],得到结果。

注意:

  1. 其中由Si派生出的优美子数组数目=Si“前缀”个数*Si“后缀”个数*;
  2. “前缀”个数=tmp[i]-tmp[i-1]
  3. “后缀”个数=tmp[i+k]-tmp[i+k-1]
  4. 遍历时,首尾可能出现边界情况,要特殊考虑。

3. 代码

时间复杂度O(n),空间复杂度O(n)。

int numberOfSubarrays(int* nums, int numsSize, int k){
    if(numsSize==0||k>numsSize)return 0;
    int tmp[numsSize];
    int cnt=0,num=0;
    //查找所有的奇数,用tmp记录其在数组中的位置
    for(int i=0;i<numsSize;i++){
        if(nums[i]&1)tmp[cnt++]=i;
    }
    //如果奇数的个数小于k,返回0;
    //--------------------------------------------------------------//
    //nums: 2 2 2 1 2 2 1 2 2 2
    //tmp:        3     6
    //j:          0     1
    //取出tmp[0]和tmp[1]之间的部分,看做整体,记为S1,在这个整体的基础上加前缀或者后缀
    //上例中前缀可能为{},{2},{2 2},{2 2 2},后缀可能为{},{2},{2 2},{2 2 2};
    //其实可以看出,前缀的个数=tmp[i]-tmp[i-1];后缀的个数=tmp[i+k]-tmp[i+k-1];
    //二者相乘得到以此S1为整体的所有可能数组的个数
    if(cnt<k)return 0;
    else if(cnt==k) num=(tmp[0]+1)*(numsSize-tmp[cnt-1]);
    else{
        for(int i=0;i<=cnt-k;i++){
            if(i==0) num+=((tmp[0]+1)*(tmp[k]-tmp[k-1]));
            else if(i==cnt-k) num+=(tmp[i]-tmp[i-1])*(numsSize-tmp[i+k-1]);
            else num+=(tmp[i]-tmp[i-1])*(tmp[i+k]-tmp[i+k-1]);
        }
    }
    return num;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值