题目描述
给定一个无序数组arr,其中元素可正、可负、可0。给定一个整数k,求arr所有的子数组中累加和小于或等于k的最长子数组长度。
示例
例如:arr = 3, -2, -4, 0, 6, k = -2,相加和小于等于-2的最长子数组为(3, -2, -4, 0),所以结果返回4。
要求
时间复杂度为O(n),空间复杂度为O(n)。
输入描述
第一行两个整数N, k,N表示数组长度,k的定义已在题目描述中给出。
第二行N个整数表示数组内的数。
输出描述
输出一个整数表示答案。
补充说明
1 ≤ N ≤ 10^5
-10° ≤ k ≤ 10° (注:原文本如此,可能意谓-10^9 ≤ k ≤ 10^9)
-100 ≤ arri ≤ 100 (注:原文本为"-100 ≤ arr ≤ 100",可能指数组元素范围)
示例输入输出
示例1:
输入:
5 -2
3 -2 -4 0 6
输出:
4
思路
双指针遍历(l和r):
l指针:遍历前缀和数组的起始位置(0到n)
r指针:在minb数组上移动,寻找满足条件minbr - bl <= k的最远位置
移动逻辑:
对于每个l,不断右移r,直到minbr - bl > k或r越界
此时,r-1是满足条件的最远位置
子数组长度计算:
子数组对应原数组下标l, r-2
长度 = (r-2) - l + 1 = r - l - 1
示例
示例说明
以示例arr = 3, -2, -4, 0, 6, k = -2为例:
前缀和数组b:
b0 = 0
b1 = 3
b2 = 3 + (-2) = 1
b3 = 1 + (-4) = -3
b4 = -3 + 0 = -3
b5 = -3 + 6 = 3
minb数组(从后往前计算):
minb5 = b5 = 3
minb4 = min(b4, minb5) = min(-3, 3) = -3
minb3 = min(b3, minb4) = min(-3, -3) = -3
minb2 = min(b2, minb3) = min(1, -3) = -3
minb1 = min(b1, minb2) = min(3, -3) = -3
minb0 = min(b0, minb1) = min(0, -3) = -3
双指针遍历过程:
l=0:
r从0开始移动:
r=0: minb0-b0=-3-0=-3 <= -2→ r=1
r=1: minb1-b0=-3-0=-3 <= -2→ r=2
r=2: minb2-b0=-3-0=-3 <= -2→ r=3
r=3: minb3-b0=-3-0=-3 <= -2→ r=4
r=4: minb4-b0=-3-0=-3 <= -2→ r=5
r=5: minb5-b0=3-0=3 > -2→ 停止
子数组长度 = r - l - 1 = 5 - 0 - 1 = 4
l=1到5:无法找到更长满足条件的子数组
结果:最大长度4(对应子数组3, -2, -4, 0)
Code
java
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
int n = in.nextInt();
int k = in.nextInt();
int[] a = new int[n];
int[] b = new int[n+1];
for(int i=0;i<n;i++) {
a[i] = in.nextInt();
if(i == 0) b[i+1] = a[i];
else b[i+1] = b[i] + a[i];
}
int[] minb = new int[n+1]; // minb[i]标识从[i, n]区间内的最小前缀和, 是单调递减的。
minb[n] = b[n];
for(int i=n-1;i>=0;i--) {
minb[i] = Math.min(b[i], minb[i+1]);
}
int res = 0;
int r = 0;
for(int l=0;l<=n;l++) {
while(r <= n && minb[r] - b[l] <= k) r ++;
if(r > l) {
res = Math.max(res, r-2-l+1); // r-1才是满足条件的,但是minb下标和a错一位,所以真实下标为r-2
}
}
System.out.println(res);
}
}