Given an array A of non-negative integers, return the maximum sum of elements in two non-overlapping (contiguous) subarrays, which have lengths L and M. (For clarification, the L-length subarray could occur before or after the M-length subarray.)数组
Formally, return the largest V for which V = (A[i] + A[i+1] + ... + A[i+L-1]) + (A[j] + A[j+1] + ... + A[j+M-1])
and either:bash
Example 1:app
Input: A = [0,6,5,2,2,5,1,9,4], L = 1, M = 2
Output: 20
Explanation: One choice of subarrays is [9] with length 1, and [6,5] with length 2.
复制代码
Example 2:函数
Input: A = [3,8,1,3,2,1,8,9,0], L = 3, M = 2
Output: 29
Explanation: One choice of subarrays is [3,8,1] with length 3, and [8,9] with length 2.
复制代码
Example 3:ui
Input: A = [2,1,5,6,0,9,5,0,3,8], L = 4, M = 3
Output: 31
Explanation: One choice of subarrays is [5,6,0,9] with length 4, and [3,8] with length 3.
复制代码
给出一个正整数数组 A,并给出两个数值 L、M; 求这个数组中长度为 L 和 M 的,且无重复元素的子数组的元素之和的最大值。spa
也就是求 V = (A[i] + A[i+1] + ... + A[i+L-1]) + (A[j] + A[j+1] + ... + A[j+M-1])
的最大值,其中翻译
而且 i、j、L、M 知足:code
/** * @param {number[]} A * @param {number} L * @param {number} M * @return {number} */
var maxSumTwoNoOverlap = function(A, L, M) {
const prefixSum = [], length = A.length
prefixSum[0] = A[0]
for (let i=1; i<length; i++) {
prefixSum[i] = prefixSum[i-1] + A[i]
}
if (L + M === length){
return prefixSum[length - 1];
}
return Math.max(split_and_find_max_sum(A, prefixSum, L, M),
split_and_find_max_sum(A, prefixSum, M, L));
};
var split_and_find_max_sum = (A, prefixSum, L, M) => {
const length = A.length
const leftMax = new Array(length), rightMax = new Array(length);
for (let i = L-1; i<length; i++) {
const tmpSum = prefixSum[i] - prefixSum[i - L + 1] + A[i - L + 1];
if (i === L - 1) {
leftMax[i] = tmpSum;
} else {
leftMax[i] = Math.max(leftMax[i - 1], tmpSum);
}
}
for (let i = length - M; i >= 0; i--) {
const tmpSum = prefixSum[i + M - 1] - prefixSum[i] + A[i];
if (i === length - M) {
rightMax[i] = tmpSum;
} else {
rightMax[i] = Math.max(rightMax[i + 1], tmpSum);
}
}
let sum = -Infinity
for (let i = L - 1; i < length - M; i++) {
sum = Math.max(sum, leftMax[i] + rightMax[i + 1]);
}
return sum
}
复制代码
这道题须要找到子数组元素和的最大值,那么确定就须要求不少不少不少不一样子数组的元素和,而后进行比对。orm
每次都将选出来的子数组元素逐一相加确定是不合算的。ip
这里的方法是,创建一个 prefixSum 数组:
const prefixSum = [];
prefixSum[0] = A[0];
for (let i=1; i<length; i++) {
prefixSum[i] = prefixSum[i-1] + A[i]
}
复制代码
这样,对于数组 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
,若是咱们想求 2-7 这个子数组的和,只须要用 prefixSum[6] - prefixSum[0]
就好了。
这道题最容易想到的方法其实就是 —— 暴力搜索:
即,将全部长度为 L 和 M 的子数组都找出来,两两配对,每一对都判断一下它们是否是重合,若是重合就舍弃;若是没有重合部分,就计算这两个数组的和,而后找出最大值便可。
可是,效率真的低啊。。。虽然打着开心的 Accepted,可是。。。
Runtime:
100 ms, faster than 16.16% of JavaScript online submissions
for Maximum Sum of Two Non-Overlapping Subarrays.
复制代码
不爽 =。=
因此该怎么办呢?
不用暴力搜索,那么就要聪明的搜索。 这道题有个很关键的能够「聪明搜索」的点,就是 non-overlapping
:既然必须是无重合的数组对,咱们一开始只须要寻找无重合的配对来求和就能够了。
如何寻找?
...怎么才能让同桌的领地和本身的领地不要重合呢?...画个三八线呗 =。=
同理,在数组中找一条**「分界线」**,在这条线两边分别寻找子数组,并让每一个子数组的求和尽量大。 最后找到全部分界状况下的子数组求和的最大值,就是答案了。
例如:有一个数组,[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
,选取数字 4,5 之间做为分界线。那么两个子数组 C、D 分别在 [1, 2, 3, 4]
和 [5, 6, 7, 8, 9, 10]
中寻找,就必定能保证数组 C、D 不重合。同理,咱们还能够把分界线选在 6,7 之间...
这样,比暴力搜索就节省了不少次无用的比较。
函数 split_and_find_max_sum
其实就是在作画分割线 & 寻找最大可能的和这件事情。
不过在函数中,它先分别从前日后(从后往前)计算了全部可能分界线的左边(右边)可能子元素的最大值。
这个是从前日后找:
for (let i = L-1; i<length; i++) {
const tmpSum = prefixSum[i] - prefixSum[i - L + 1] + A[i - L + 1];
if (i === L - 1) {
leftMax[i] = tmpSum;
} else {
leftMax[i] = Math.max(leftMax[i - 1], tmpSum);
}
}
复制代码
这个 leftMax 就能够认为是,当分界线选了 i,i+1 之间的时候,分界线左边全部长度为 L 的子数组元素求和的最大值
这个是从后往前找:
for (let i = length - M; i >= 0; i--) {
const tmpSum = prefixSum[i + M - 1] - prefixSum[i] + A[i];
if (i === length - M) {
rightMax[i] = tmpSum;
} else {
rightMax[i] = Math.max(rightMax[i + 1], tmpSum);
}
}
复制代码
rightMax 和 leftMax 基本同理。只不过 rightMax 选子数组的范围是线右边,同时数组长度是 M。
最后一轮 for 循环才是在比对全部分界线状况,并找出最大值。
let sum = -Infinity
for (let i = L - 1; i < length - M; i++) {
sum = Math.max(sum, leftMax[i] + rightMax[i + 1]);
}
return sum
复制代码
函数 split_and_find_max_sum
须要调用两次,缘由是两个子数组长度可能不一样,谁在线左边,谁在线右边,须要分状况讨论。