Round 42: 统计得分小于K的子数组数目
题目来源:Leetcode双周赛80
标签:前缀和+双指针
难度:Hard
一个数字的 分数 定义为数组之和 乘以 数组的长度。
比方说,[1, 2, 3, 4, 5] 的分数为 (1 + 2 + 3 + 4 + 5) * 5 = 75 。
给你一个正整数数组 nums 和一个整数 k ,请你返回 nums 中分数 严格小于 k 的 非空整数子数组数目。
子数组 是数组中的一个连续元素序列。
输入:nums = [2,1,4,3,5], k = 10
输出:6
解释:
有 6 个子数组的分数小于 10 :
- [2] 分数为 2 * 1 = 2 。
- [1] 分数为 1 * 1 = 1 。
- [4] 分数为 4 * 1 = 4 。
- [3] 分数为 3 * 1 = 3 。
- [5] 分数为 5 * 1 = 5 。
- [2,1] 分数为 (2 + 1) * 2 = 6 。
注意,子数组 [1,4] 和 [4,3,5] 不符合要求,因为它们的分数分别为 10 和 36,但我们要求子数组的分数严格小于 10 。
根据分数的公式:Sum∗lenSum * lenSum∗len,可以想到需要先求 前缀和 ,方便快速地计算某个区间的和。
然后我们采用双指针,不断移动双指针的右指针知道区间的 分数 大于等于 k,此时计算该区间有多少个符合条件的子数组。
注意:可能会存在重复计算的情况,因此要将重复的删除

class Solution {
public long countSubarrays(int[] nums, long k) {
int n = nums.length;
long [] preSum = new long[n+1];
for (int i = 1;i <= n;i++){ // 计算前缀和
preSum[i] = preSum[i-1] + nums[i-1];
}
long ans = 0;
int pre_r = -1; // 记录上一个区间的有边界,用于判断是否需要删除重复计算的的子数组
for (int l = 1;l <= n;l++){
int r;
// 时间优化,不用每次都从 r = l开始判断,直接从上次的pre_r开始
if (pre_r != -1)
r = pre_r;
else
r = l;
// 如果满足区间分数小于k,右移r
while (r <= n && (preSum[r] - preSum[l-1]) * (long)(r - l + 1) < k){
r += 1;
}
// 计算此区间的符合条件子数组个数
ans += (long)(r - l) * (long)(r - l + 1) / 2;
if (pre_r != -1 && l < pre_r){ // 如果有重复数组 则删除重复数组
ans -= (long)(pre_r - l) * (long)(pre_r - l + 1) / 2;
}
pre_r = r; // 更新有边界
if (r > n){
break; // 如果r > n说明全部判断完了,直接结束
}
}
return ans;
}
}
博客围绕Leetcode双周赛80中的题目,即统计得分小于K的子数组数目展开。题目难度为Hard,分数定义为数组之和乘以长度。解题思路是先求前缀和以快速计算区间和,再用双指针移动右指针,计算符合条件子数组数目,同时要避免重复计算。
462

被折叠的 条评论
为什么被折叠?



