第499题 非重叠子数组的最大和
1.题目分析
题目要求在一个整数数组 nums 中,找到两个长度分别为 firstLen 和 secondLen 的非重叠子数组,使得这两个子数组的元素和最大。
需要注意的是:
- 这两个子数组不能有任何重叠部分。
- 子数组的顺序没有限制,即长度为
firstLen的子数组可以在secondLen的子数组前,也可以在其后。
因此,我们需要找到一个算法,可以分别考虑两种情况,并得到最大和。
2.解题思路
解题思路可以分为以下几个步骤:
-
先分别计算
firstLen和secondLen长度的子数组在不同位置的最大和。具体来说:- 计算数组中从头到尾每个位置的前
firstLen长度的子数组的最大和。 - 计算数组中从尾到头每个位置的前
secondLen长度的子数组的最大和。 - 将上述步骤反过来再做一次,即计算
secondLen在前、firstLen在后的情况。
- 计算数组中从头到尾每个位置的前
-
最后,遍历数组,分别尝试两种组合:
firstLen子数组在前,secondLen子数组在后。secondLen子数组在前,firstLen子数组在后。
-
取两种组合中的最大值,得到结果。
3.解题代码
def solution(nums: list, firstLen: int, secondLen: int) -> int:
def max_sum_subarray(arr, k):
# 初始化第一个窗口的和
max_sum = sum(arr[:k])
current_sum = max_sum
max_sums = [0] * len(arr)
max_sums[k - 1] = max_sum
# 滑动窗口遍历数组,更新每个位置的最大和
for i in range(k, len(arr)):
current_sum += arr[i] - arr[i - k]
max_sum = max(max_sum, current_sum)
max_sums[i] = max_sum
return max_sums
# 计算 leftMax 和 rightMax 数组,用于组合计算
left_first = max_sum_subarray(nums, firstLen)
right_second = max_sum_subarray(nums[::-1], secondLen)[::-1]
left_second = max_sum_subarray(nums, secondLen)
right_first = max_sum_subarray(nums[::-1], firstLen)[::-1]
max_sum = 0
# firstLen 在前,secondLen 在后
for i in range(firstLen - 1, len(nums) - secondLen):
max_sum = max(max_sum, left_first[i] + right_second[i + 1])
# secondLen 在前,firstLen 在后
for i in range(secondLen - 1, len(nums) - firstLen):
max_sum = max(max_sum, left_second[i] + right_first[i + 1])
return max_sum
if __name__ == '__main__':
print(solution(nums=[0, 6, 5, 2, 2, 5, 1, 9, 4], firstLen=1, secondLen=2) == 20)
print(solution(nums=[3, 8, 1, 3, 5, 2, 1, 0], firstLen=3, secondLen=2) == 21)
print(solution(nums=[2, 1, 4, 3, 5, 9, 5, 0, 3, 8], firstLen=4, secondLen=3) == 33)
4.模块解释
4.1 max_sum_subarray 函数
-
功能:计算每个位置的前
k个元素的最大和。 -
实现细节:
- 使用滑动窗口方法,先计算第一个窗口的和。
- 然后滑动窗口,从第
k个元素开始计算窗口和,并记录每个位置的最大和。 - 最后返回一个数组
max_sums,其中max_sums[i]表示从数组开始到位置i的最大子数组和。
4.2 left_first 和 right_second
-
功能:分别计算
firstLen和secondLen长度的子数组的最大和,并考虑顺序。 -
实现细节:
left_first:使用max_sum_subarray计算firstLen长度的子数组在从头开始到当前位置的最大和。right_second:使用max_sum_subarray计算secondLen长度的子数组从尾部到当前位置的最大和(通过逆序数组再逆序回来实现)。- 这样可以将
firstLen在前,secondLen在后的情况组合起来。
4.3 left_second 和 right_first
-
功能:类似于
left_first和right_second,但考虑secondLen在前,firstLen在后的情况。 -
实现细节:
left_second:计算secondLen长度的子数组的最大和在从头开始到当前位置的最大和。right_first:计算firstLen长度的子数组从尾部到当前位置的最大和。- 这样可以将
secondLen在前,firstLen在后的情况组合起来。
4.4 最大和计算
-
功能:遍历数组,分别考虑两种组合方式,得到最大和。
-
实现细节:
- 第一种情况是
firstLen子数组在前,secondLen子数组在后。我们使用left_first[i] + right_second[i + 1]表示。 - 第二种情况是
secondLen子数组在前,firstLen子数组在后。我们使用left_second[i] + right_first[i + 1]表示。 - 取这两种情况中的最大值作为结果返回。
- 第一种情况是
5.结论
该算法使用滑动窗口和前缀和的思想,分别计算了两种顺序的非重叠子数组最大和。通过对 firstLen 和 secondLen 的顺序进行分情况处理,确保找到了最大和的组合。最终的时间复杂度为 O(n)O(n)O(n),因为我们只需要遍历数组两次。算法效率较高,能够满足大多数题目的规模要求。