LeetCode——第十八天
1248. 统计「优美子数组」
给你一个整数数组 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
提示:
1 <= nums.length <= 50000
1 <= nums[i] <= 10^5
1 <= k <= nums.length
分析:一次循环遍历,求出每个最优子数组,再去判断一个个遍历显然是会超时。这边自己想到就是找个k个奇数,然后判断前后偶数的个数,相乘就是这一个区间的最优子数组个数,不断遍历到结束,不过写起来比较繁琐一点,官方给出了增加一个数组来存放奇数下标,就不需求另外用四个变量去存放修改位置了。
odd数组代码
class Solution {
public:
int numberOfSubarrays(vector<int>& nums, int k) {
int n = (int)nums.size();
int odd[n+2], res = 0, count = 0;//odd用于存放出现奇数的下标,count计数
//遍历存放下标
for(int i=0;i<n;i++){
if(nums[i]%2) odd[++count] = i;
}
//首尾即边界情况处理
odd[0] = -1;
odd[++count] = n;
for(int i=1;i+k<=count;i++){//这里是遍历odd数组,从1开始
res+=(odd[i]-odd[i-1])*(odd[i+k]-odd[i+k-1]);
//第一次出现奇数和上一次之间以及第k+1个奇数到第k个奇数之间分别有多少个偶数
//例如题目11211,假设k是3,那么上面就是odd[1]-odd[0]=0-(-1)=1
//后面是odd[i+k]-odd[i+k-1],就是odd[4]-odd[3]=4-3=1,结果是1*1=1
}
return res;
}
};
官方第二个前缀和太秀了,具体备注写了些,写的有点乱,下面是官网说明。
另外前缀和+差分具体可以看看这两篇博客(第一篇有解释点,第二篇偏代码和应用):
前缀和差分
前缀和、二维前缀和与差分的小总结
前缀和代码
class Solution {
public:
int numberOfSubarrays(vector<int>& nums, int k) {
int n = nums.size();
vector<int> cnt(n+1,0);//cnt用于存放某个奇数个数出现的次数
int odd = 0, res = 0;//odd存储奇数下标
cnt[0] = 1;
for(int i=0;i<n;i++){
odd+=nums[i]&1;
/*
如果是奇数则个数加1,否则不变,可以用if加%方式来判断
这边是按位与运算,偶数最后一位肯定是0
*/
res+=odd>=k?cnt[odd-k]:0;
cnt[odd]+=1;
/*
先说cnt,比如题目11211,i到3时候,此时对应odd为1,2,3
对应就是cnt[1]=1,cnt[2]=2,cnt[3]=1,即出现奇数次数为1的情况有1次
这边2出现两次,因为1121,在2这里又出现一次,这样就可以统计出所以奇数出现次数的情况
*/
}
return res;
}
};
2.总结
这题目第一种应该很多都知道,就是边界情况要处理,第二种反正我不是很会。