【Lintcode】404. Subarray Sum II

    科技2022-07-13  127

    题目地址:

    https://www.lintcode.com/problem/subarray-sum-ii/description

    给定一个正整数数组 A A A,再给定一个区间 [ s , e ] [s,e] [s,e],问 A A A有多少个子数组之和位于那个区间内。

    思路是前缀和。先把 A A A的前缀和 p p p算出来,其中 p [ i ] = ∑ k = 0 i − 1 A [ k ] p[i]=\sum_{k=0}^{i-1}A[k] p[i]=k=0i1A[k],规定 p [ 0 ] = 0 p[0]=0 p[0]=0。对于 i < j i<j i<j,就对应了一个子数组的和,如果 e ≥ p [ j ] − p [ i ] ≥ s e\ge p[j]-p[i]\ge s ep[j]p[i]s,可以枚举 j j j,则 p [ j ] − s ≥ p [ i ] ≥ p [ j ] − e p[j]-s\ge p[i]\ge p[j]-e p[j]sp[i]p[j]e。由于 A A A恒正,所以 p p p是严格单调增的,所以可以用二分的方式搜索出 i i i的范围。代码如下:

    public class Solution { /** * @param A: An integer array * @param start: An integer * @param end: An integer * @return: the number of possible answer */ public int subarraySumII(int[] A, int start, int end) { // write your code here // 求前缀和 int[] preSum = new int[A.length + 1]; for (int i = 0; i < A.length; i++) { preSum[i + 1] = preSum[i] + A[i]; } // 统计答案 int res = 0; for (int i = 1; i < preSum.length; i++) { int left = 0, right = 0; // 找第一个大于等于target的数的下标 int target = preSum[i] - end; int l = 0, r = i - 1; while (l < r) { int m = l + (r - l >> 1); if (preSum[m] >= target) { r = m; } else { l = m + 1; } } if (preSum[l] < target) { continue; } else { left = l; } // 找最后一个小于等于target的数的下标 l = 0; r = i - 1; target = preSum[i] - start; while (l < r) { int m = l + (r - l + 1 >> 1); if (preSum[m] <= target) { l = m; } else { r = m - 1; } } if (preSum[l] > target) { continue; } else { right = l; } // 两个都找到了,累加个数 res += right - left + 1; } return res; } }

    时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn),空间 O ( n ) O(n) O(n)

    Processed: 0.011, SQL: 8