前言
对于连续子序列问题,可以采用双指针来解决,同时也可以练习前缀和+二分。
一、统计得分小于 K 的子数组数目
二、题解
1、前缀和+二分
public class CountSubarrays {
/*
target:取连续子序列,判断子序列得分是否小于k,记录这样的子序列个数。
要求连续,所以可用前缀和 判断得分序列,满足条件的话,它的子连续序列也是ok的,所以二分查找到最大得分子序列。
M1:前缀和+抽象二分。
*/
public long countSubarrays(int[] nums, long k) {
long[] prefix = new long[nums.length + 1];
// bug2:前缀和溢出
// int sum = 0;
long sum = 0;
for (int i = 1; i <= nums.length; i++) prefix[i] = (sum += nums[i - 1]);
//bug1:精度不够。
//int rs = 0;
long rs = 0;
for (int i = 1; i <= nums.length; i++) {
int idx = binarySearch(prefix, i, k);
rs += (idx - i);
}
return rs;
}
// 抽象二分
private int binarySearch(long[] prefix, int i, long k) {
int low = i, high = prefix.length;
while (low < high) {
int mid = low + (high - low >>> 1);
long midVal = prefix[mid] - prefix[i - 1];
if (midVal * (mid - i + 1) >= k) high = mid;
else low = mid + 1;
}
return high;
}
}
2、双指针
// 双指针,双指针定住一个满足条件的连续子序列,那么比它短的子序列也是OK的。
// 注:前缀和一般处理连续子序列问题,可多想想双指针,不一定要前缀+二分。
class CountSubarrays2 {
/*
target:取连续子序列,判断子序列得分是否小于k,记录这样的子序列个数。
要求连续,用双指针定住连续子序列。
M2:双指针。
*/
public long countSubarrays(int[] nums, long k) {
long rs = 0, sum = 0l;
//双指针
int right = 0;
for (int i = 0; i < nums.length; i++) {
while (right < nums.length) {
if ((sum + nums[right]) * (right - i + 1) >= k) break;
sum += nums[right++];
}
long gap = right - i;
rs += gap;
sum -= nums[i];
}
// 返回结果
return rs;
}
}
总结
1)连续子数组与前缀和/二分法/双指针。