1. 题目描述
如图所示:
2. 题目理解
看到这个“子数组”的概念,首先会想到,如果数组中存在优美子数组,那么会有很多“子数组”都是由一些最基本的“子数组”派生而来的,这些最基本的“子数组”,应该是包含k个奇数,并且元素数目最少的那些数组,不妨记为Si。则从Si可以向前或者向后包含序列中的偶数(如果存在),进而派生出其他的“子数组”。
有了这个思路之后,就明确了基本方向:
- 先找数组序列中所有的奇数,记录奇数元素的下标tmp[i],如果找到的奇数个数小于k,直接return 0;
- 从前到后,找到包含k个奇数并且元素数目最少的子数组,将其作为整体Si;Si由nums[]中下标从tmp[i]到tmp[i+k-1]的元素组成;
- 在Si前后加上“前缀”或者“后缀”,可以得到由此子数组派生出的所有“优美子数组”。
- 遍历tmp[],得到结果。
注意:
- 其中由Si派生出的优美子数组数目=Si“前缀”个数*Si“后缀”个数*;
- “前缀”个数=tmp[i]-tmp[i-1];
- “后缀”个数=tmp[i+k]-tmp[i+k-1];
- 遍历时,首尾可能出现边界情况,要特殊考虑。
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;
}