统计「优美子数组」
难度:
标签:
题目描述
代码结果
运行时间: 574 ms, 内存: 22.7 MB
/*
* 思路:
* 我们需要找到包含恰好 k 个奇数的子数组的数量。
* 通过使用Java Stream API,我们可以更优雅地实现这个问题。
* 首先找到所有奇数的位置,然后计算有多少子数组可以包含这些奇数。
* 对于每个窗口(包含 k 个奇数),我们可以通过计算其左边和右边的偶数数目来确定有多少个这样的子数组。
*/
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
public class BeautifulSubarraysStream {
public int numberOfSubarrays(int[] nums, int k) {
List<Integer> oddIndices = IntStream.range(0, nums.length)
.filter(i -> nums[i] % 2 != 0)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
if (oddIndices.size() < k) {
return 0;
}
return IntStream.range(0, oddIndices.size() - k + 1)
.map(i -> {
int left = (i == 0) ? oddIndices.get(i) + 1 : oddIndices.get(i) - oddIndices.get(i - 1);
int right = (i + k == oddIndices.size()) ? nums.length - oddIndices.get(i + k - 1) : oddIndices.get(i + k) - oddIndices.get(i + k - 1);
return left * right;
})
.sum();
}
public static void main(String[] args) {
BeautifulSubarraysStream bs = new BeautifulSubarraysStream();
int[] nums = {1, 1, 2, 1, 1};
int k = 3;
System.out.println(bs.numberOfSubarrays(nums, k)); // 输出 2
}
}
解释
方法:
此题解首先统计数组中所有奇数的位置,并将这些位置的索引存储在一个列表`odd`中。为了方便处理边界情况,题解在`odd`列表的开头添加了一个-1(表示数组起始之前的位置),在末尾添加了`len(nums)`(表示数组结尾之后的位置)。接下来,题解通过遍历`odd`中的每个有效奇数位置的索引来计算每种可能的子数组的数量。对于每个有效的索引`i`(从1到`len(odd)-k`),计算从`odd[i-1]+1`到`odd[i+k-1]`的子数组,其中包含恰好`k`个奇数。通过计算两端奇数位置之间的间隔,可以确定每个这样的子数组可以在原数组中存在的不同位置数,最后将所有这些可能的位置数累加起来,得到最终结果。
时间复杂度:
O(n)
空间复杂度:
O(n)
代码细节讲解
🦆
在题解中添加-1和len(nums)作为数组的起始和结束辅助点的原因是什么?这样做有什么特别的好处吗?
▷🦆
题解中提到的方法在计算可能的子数组位置时使用的是两侧奇数位置差的乘积,请问这种计算方式的原理是什么?
▷🦆
为什么题解在遍历数组时只记录奇数的索引,而不是直接在遍历时就计算子数组的数量?
▷🦆
题解算法是否需要对输入数组`nums`的长度或者数组中元素的值进行特殊处理或者验证?
▷