题目地址:
https://www.lintcode.com/problem/pen-box/description
给定一个长 n n n的正整数数组 A A A,再给定一个正整数 t t t,要求找到两个不重合的子区间,使得各自和都是 t t t,并且总长度最小。如果解不存在则返回 − 1 -1 −1。
思路是前缀和 + 双指针。先求出前缀和数组 p p p,然后预处理一个数组 l l l使得 l [ i ] l[i] l[i]是 A [ 0 : i ] A[0:i] A[0:i]内和为 t t t的子数组的最小长度。由于 A A A里都是正数,所以 p p p单调,所以可以用前后双指针来做。预处理完毕之后,只需再从右向左再用前后双指针枚举以 A [ i ] A[i] A[i]开头的最短的和 t t t的子数组,将其长度加上 l [ i − 1 ] l[i-1] l[i−1],就得出了以 A [ i ] A[i] A[i]为第二个区间左端点的最短总长度。枚举完第二区间左端点,就得到了全局最优解。代码如下:
public class Solution {
/**
* @param boxes: number of pens for each box
* @param target: the target number
* @return: the minimum boxes
*/
public int minimumBoxes(int[] boxes, int target) {
// write your code here
int len = boxes.length;
int[] lShort = new int[len], preSum = new int[len + 1];
// 求前缀和
for (int i = 0; i < boxes.length; i++) {
preSum[i + 1] = preSum[i] + boxes[i];
}
// 预处理lShort数组,lShort[i]是A[0 : i]的最短的和为target的子数组长度
for (int i = 1, j = 0; i <= len; i++) {
while (preSum[i] - preSum[j] > target) {
j++;
}
if (preSum[i] - preSum[j] == target) {
lShort[i - 1] = i - j;
} else if (i - 1 > 0) {
lShort[i - 1] = lShort[i - 2];
}
}
int res = len + 1;
// 再从右向左求以A[i]开头的最短和target的子数组长度,加上lShort[i - 1]做枚举
for (int i = len - 1, j = len; i >= 1; i--) {
while (preSum[j] - preSum[i] > target) {
j--;
}
// 这里注意要判断一下lShort[i - 1]不是0,因为有可能左区间不存在,只有在存在的情况下才能参与计算
if (preSum[j] - preSum[i] == target && lShort[i - 1] != 0) {
res = Math.min(res, lShort[i - 1] + j - i);
}
}
return res == len + 1 ? -1 : res;
}
}
时空复杂度 O ( n ) O(n) O(n)。