累加和小于等于 aim 的最长子数组

本文介绍了一种求解数组中累加和小于等于目标值的最长子数组长度的算法。通过使用两个辅助数组min_num和min_index,分别记录以当前位置为左边界能取得的最小累加和及对应的右边界,从而高效地找出满足条件的最长子数组。

题目:

给定一个数组 arr 和目标值 aim,求该数组的子数组中累加和小于等于 aim 的最长子数组长度

思路

准备两个辅助数组,一个是 min_num, 其中存放的是以当前位置为左边界,能够取得的最小累加和是多少;另一个辅助数组是 min_index ,存放的是 该位置取得最小累加和时候的右边界位置。

首先考虑两个辅助数组的求解, min_num 因为求的是以当前位置为左边界的最小累加和,所以我们可以考虑从右往左求解,最后一个位置存放的当然就是 arr 中最后一个数本身,相应的 min_index 存放的也就是该数的位置。
至于其他位置,可以这样考虑。因为求解的是最小累加和,所以如果 min_num 中,当前位置的右边的相邻位置的最小累加和是一个正数,那么 这种情况下,当前位置的最小累加和只能是 arr 中当前位置的数本身了,因为 min_num 中存放的已经是最小累加和了,而如果前面的最小累加和是一个正数,那么当前位置的数再加上一个正数一定会越来越大,不会变小,所以最小的累加和就是当前数本身。而如果前面的最小累加和是一个负数,那么最小累加和就是当前数加上这个负数,道理也是一样。

而 min_index,就更简单了,如果累加和是当前数本身,那么右边界自然就是当前数的位置,如果是当前数加上前面的累加和,那右边界就是前面取得最小累加和时的右边界。

有了两个辅助数组的信息之后,事情就变得简单起来。

从头开始遍历数组,从辅助数组中分别取出当前位置的 最小累加和和相应的右边界,如果该累加和小于等于 aim , 从右边界的下一个位置开始,继续取出该位置的 最小累加和 以及右边界,将两个累加和相加,判断此时的 累加和是否 小于等于 aim ,

  1. 如果不满足了,则当前的右边界就是 arr 数组第一个位置的累加和小于等于 aim 的最长子数组 的右边界,减去 当前的 arr 位置,就是子数组的长度。接着求解 arr 数组下一个位置的最长子数组,不断更新子数组的长度。
  2. 如果还满足,就一直向右扩,直到不满足条件为止,或者 arr 到头了。

代码

def get_max_subArr(arr, aim):

    min_num, min_index = [0] * len(arr), [0] * len(arr)
    index = len(arr) - 1
    min_num[-1], min_index[-1] = arr[-1], index
    index -= 1
    while index < 0:
        if min_num[index + 1] < 0:
            min_num[index] = arr[index] + min_num[index + 1]
            min_index[index] = min_index[index + 1]
        else:
            min_num[index] = arr[index]
            min_index[index] = index
        index -= 1

    sum_res = 0
    max_length = 0
    right = 0

    for start in range(len(arr)):
        while right < len(arr) and sum_res + min_num[right] <= aim:
            right = min_index[right]
            sum_res += min_num[right]
            right += 1

        sum_res -= arr[start] if right > start else 0 # 如果当前start 大于等于右边界,说明sum_res中就没有值
        max_length = max(max_length, right - start)
        right = max(right, start + 1)  # start值直接大于aim值,扩不动,例如[200, 100, 6, 3, 2, 1], aim=5,这是保证了
        # 右边界一直在 start 的右边

    return max_length
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值