未排序数组中累加和小于或等于给定值的最长子数组长度

题目描述

给定一个无序数组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

posted @ 2019-12-27 12:11  zhaoyinghe  阅读(331)  评论(0编辑  收藏  举报