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个奇数,且数组的左右两端都是奇数,比如数组1,2,2,1,1,k=3,这样的数组中,肯定只有1个子数组满足题意条件。即数组本身。
现在考虑在数组两端添加一些偶数,比如2,1,2,2,1,1,2,k=3。这样的数组可以有4个子数组满足题意。即分别加上左边的0,1个偶数组成子数组,加上右边的0,1个偶数组成子数组。
观察可以得知,在这种条件下,数组中优美子数组的个数为(左边偶数个数+1)*(右边偶数个数+1)
考虑更一般的情况,我们可以记录一下数组中每个奇数的缝隙直接插了多少个偶数。设数组中奇数总个数为n,则有数组even[],even[0]表示数组最左端的偶数个数,even[1]表示第一个奇数和第二个奇数之间的偶数个数,even[n]表示数组最后一个奇数右端的偶数个数。则第i个奇数左端有even[i-1]个偶数,第i+k个奇数右端有even[k]偶数。循环遍历even数组即得答案。
点击查看代码
class Solution {
public:
int numberOfSubarrays(vector<int>& nums, int k) {
vector<int> even;
int evenCount = 0, res = 0;
for(int i = 0; i < nums.size(); i++)
{
if(nums[i] % 2)
{
even.push_back(evenCount);
evenCount = 0;
if(i == nums.size() - 1) even.push_back(evenCount);
}
else if(i == nums.size() - 1){
evenCount++;
even.push_back(evenCount);
}
else
evenCount++;
}
for(int i = 0; i + k < even.size(); i++)
res += (even[i] + 1) * (even[i + k] + 1);
return res;
}
};
解法二:
这是leetcode官方的解法,用到前缀和的思想。官方解释很难懂,我的理解是,维护一个变量odd,存放到目前位置i为止数组中出现奇数的个数。开一个cnt[]数组,记录数组中奇数个数为odd的次数。比如,现在遍历到nums[i],到i位置的奇数个数为odd,那么我们就让odd+1。i这个位置为终点时有多少子数组满足优美的条件呢?应该是cnt[odd - k](注意检查odd - k>=0)。
点击查看代码
class Solution {
public:
int numberOfSubarrays(vector<int>& nums, int k) {
vector<int> cnt(nums.size() + 1, 0);
int odd = 0, res = 0;
cnt[0] = 1;
for(int i = 0; i < nums.size(); i++)
{
odd += (nums[i] & 1);
res += (odd - k >= 0) ? cnt[odd - k] : 0;
cnt[odd] += 1;
}
return res;
}
};
两种解法时间复杂度都为o(n)。空间复杂度都为o(n)。下图中提交时间较早的是解法一。


浙公网安备 33010602011771号