Solution1: Divide and Conquer
This question asks for the three non-overlapping intervals with maximum sum. So this can be divided into 3 parts -- find 3 non-overlapping intervals respectively, and combining the results to get the 3 non-overlapping intervals we want. Since each interval is of length k, suppose the start index of the middle interval is i
, then the range of i
's value is: k <=i <= n-2k
. Actually this i
has also indicated the limits of the range of the first interval and the range of the last interval. Hence we have the following DP components:
left[i] stores the start index for the first interval in range [0, i]
right[i] stores the start index for the third interval in range [i, n - 1]
After that we can test every possible start index of middle interval, i.e. k <= i <= n - 2k
. And based on the above 2 DP results, we can easily get the first and third max sum intervals. The runtime of this process is O(n)
.
Note that this question asks for the lexicographical smallest order interval, that is, when there are two intervals with the same max sum, we always select the leftmost one. So in this solution, for the first interval, since we are iterating from left to right, the comparison if
statement use currSum > total
. And for the third interval, we are iterating from right to left, the comparison if
statement use currSum >= total
.
Another trick we use here, to ensure the algorithm runs in O(n)
time, is that we use predix sum to get the sum of a consecutive subarray in O(1)
time. Otherwise, if we calaulate the interval sum each time, that would be O(k)
time for each interval.
Run time complexity of this algorithm is O(n)
. Space complexity is O(n)
. n
is the length of the input nums
array.
class Solution {
public int[] maxSumOfThreeSubarrays(int[] nums, int k) {
int n = nums.length;
int[] sum = new int[n + 1];
int[] left = new int[n];
int[] right = new int[n];
int[] ret = new int[3];
// First get the prefix sum of nums.
// Prefix sum enables us to get the sum of k consecutive element in O(1) time
for (int i = 0; i < n; i++) {
sum[i + 1] = sum[i] + nums[i];
}
// DP for the left intetval max sum
for (int i = k, tot = sum[k] - sum[0]; i < n; i++) {
if (sum[i + 1] - sum[i - k + 1] > tot) {
tot = sum[i + 1] - sum[i - k + 1];
left[i] = i - k + 1;
} else {
left[i] = left[i - 1];
}
}
// DP for the right interval max sum
right[n - k] = n - k;
for (int i = n - 1 - k, tot = sum[n] - sum[n - k]; i >= 0; i--) {
if (sum[i + k] - sum[i] >= tot) {
tot = sum[i + k] - sum[i];
right[i] = i;
} else {
right[i] = right[i + 1];
}
}
// Find the max sum by iterating through the middle interval index based on above 2 cache.
int maxSum = 0;
for (int i = k; i <= n - 2 * k; i++) {
int l = left[i - 1], r = right[i + k];
int tot = sum[l + k] - sum[l] + sum[r + k] - sum[r] + sum[i + k] - sum[i];
if (tot > maxSum) {
ret[0] = l;
ret[1] = i;
ret[2] = r;
maxSum = tot;
}
}
return ret;
}
}
Solution2: Generalized method
This is a generalized method for 689. Maximum Sum of 3 Non-Overlapping Subarrays
.
In this solution, dp[i][j]
is used for recording for ith interval, what are the max sums for first j numbers in each position. And index[i][j]
is used for recording for ith interval, what are the current start index for first j numbers that made up the max sum.
Thus after the searching ends, dp[n][nums.length]
stores the max sum we can get and index[n][nums.length]
stores the start index of last interval for the max sum. And now we can search backwards for each previous start index based on the start index of current interval. Because the start index of previous interval is the index stored in index[i - 1][current start index]
, which is the max sum of the subarray before current start index.
Run time complexity is O(n * len)
where n
is the number of intervals needed and len is the length of input array.
Space complexity is O(n * len)
as well.
class MaxSumOfThreeSubarrays {
/**
*
* @param nums input array of numbers
* @param k length of each interval
* @param n number of intervals
* @return start index of each interval that has the maximum sum
*/
public int[] maxSumOfThreeSubarrays(int[] nums, int k, int n) {
int[][] dp = new int[n + 1][nums.length + 1];
int[][] index = new int[n + 1][nums.length + 1];
int tot = 0;
// prefix sum
int[] sum = new int[nums.length + 1];
for (int i = 0; i < nums.length; i++) {
sum[i + 1] = nums[i] + sum[i];
}
for (int i = 1; i <= n; i++) {
for (int j = k - 1; j < nums.length; j++) {
int tmpMax = sum[j + 1] - sum[j - k + 1] + dp[i - 1][j - k + 1];
if (tmpMax > dp[i][j]) {
dp[i][j + 1] = tmpMax;
index[i][j + 1] = j - k + 1;
} else {
dp[i][j + 1] = dp[i][j];
index[i][j + 1] = index[i][j];
}
}
}
int[] ret = new int[n];
int prev = nums.length;
for (int i = n; i > 0; i--) {
ret[i - 1] = index[i][prev];
prev = ret[i - 1];
}
return ret;
}
}