未排序数组中累加和小于或等于给定值的最长子数组长度
题目描述
给定一个无序数组arr,其中元素可正、可负、可0。给定一个整数k,求arr所有的子数组中累加和小于或等于k的最长子数组长度
要求
时间复杂度为O(n),空间复杂度为O(n)
示例
输入描述
第一行两个整数N, k。N表示数组长度,k的定义已在题目描述中给出
第二行N个整数表示数组内的数
输出描述
输出一个整数表示答案
示例1
输入
5 -2
3 -2 -4 0 6
输出
4
备注
\(1 \leq N \leq 10^5\)
$-10^9 \leq k \leq 10^9 \( \)-100 \leq arr_i \leq100 $
思路比较巧妙,创建一个两个数组min_sum和ind,min_sum[i]表示到从输入数组末尾到i位置为止的最小子数组和,ind[i]记录这个数组的起始位置索引,注意这里是从后往前。
参考滑动窗口思想,从左往右对于每个i,求解满足条件的最大子数组边界。
n, k = map(int, input().strip().split(' '))
nums = list(map(int, input().strip().split(' ')))
min_sum = [0]*n
ind = [0]*n
min_sum[-1] = nums[-1]
ind[-1] = n-1
for i in range(n-1)[::-1]:
min_sum[i] = min(min_sum[i+1], 0)+nums[i]
if min_sum[i+1] <= 0:
ind[i] = ind[i+1]
else:
ind[i] = i
#start表示子数组的开始,end表示结束位置后一位,s表示索引从start到end-1的子数组和
start = end = s = 0
res = 0
for start in range(n):
end = max(end, start)
# 对于每个start,end不必重新退回到新的start位置。这是因为start是向右移的,要寻找可能存在满足条件的更大的子数组的话,end只能在原有的end基础上右移,因此end不必要重新回到新的start位置。
# start看作左边界,end看作右边界+1,当start=end时,代表子数组为空。
while end < n and s+min_sum[end] <= k:
s += min_sum[end]
end = ind[end]+1
res = max(res, end-start) #记录最长的子数组
if end > start: #这里表示nums[start:end] > k,此时需要将start右移。当start移动到end-1时,此时s=0
s -= nums[start]
print(res)
参考资料
https://www.nowcoder.com/questionTerminal/3473e545d6924077a4f7cbc850408ade?f=discussion