Find three non-overlapping subarrays of length k with maximum total sum and return the lexicographically smallest starting indices.
Problem Restatement
We are given an integer array nums and an integer k.
We need to choose three non-overlapping subarrays, each with length k, so that their total sum is as large as possible.
Return the starting indices of those three subarrays.
If there are multiple answers with the same total sum, return the lexicographically smallest list of indices. The official statement asks for three non-overlapping length-k subarrays with maximum total sum and 0-indexed starting positions.
Input and Output
| Item | Meaning |
|---|---|
| Input | nums, an integer array, and integer k |
| Output | Three starting indices |
| Subarray length | Exactly k |
| Overlap rule | The three chosen subarrays cannot overlap |
| Tie rule | Return the lexicographically smallest answer |
Example function shape:
def maxSumOfThreeSubarrays(nums: list[int], k: int) -> list[int]:
...Examples
Example 1:
nums = [1, 2, 1, 2, 6, 7, 5, 1]
k = 2All chosen subarrays must have length 2.
The best choice is:
| Start | Subarray | Sum |
|---|---|---|
0 | [1, 2] | 3 |
3 | [2, 6] | 8 |
5 | [7, 5] | 12 |
Total:
3 + 8 + 12 = 23Answer:
[0, 3, 5]Example 2:
nums = [1, 2, 1, 2, 1, 2, 1, 2, 1]
k = 2Many length-2 windows have the same sum.
The lexicographically smallest valid answer is:
[0, 2, 4]First Thought: Try All Triples
A direct approach is to compute every possible length-k subarray sum.
Then try every triple of starting indices:
i, j, lwhere:
i + k <= j
j + k <= lFor each triple, compute the total sum and keep the best one.
This works, but there can be O(n) possible windows, and trying all triples costs:
O(n^3)That is too slow.
We need to reuse the best left and right choices for each possible middle subarray.
Key Insight
Fix the middle subarray.
If the middle subarray starts at index mid, then:
| Part | Valid range |
|---|---|
| Left subarray | Must start at or before mid - k |
| Middle subarray | Starts at mid |
| Right subarray | Must start at or after mid + k |
So for each possible middle index, we need:
- the best length-
kwindow on the left, - the middle window,
- the best length-
kwindow on the right.
We can precompute the best left window for every position and the best right window for every position.
Window Sums
First, compute all length-k subarray sums.
Let:
window_sum[i]be the sum of:
nums[i] + nums[i + 1] + ... + nums[i + k - 1]For:
nums = [1, 2, 1, 2, 6, 7, 5, 1]
k = 2we get:
| Start | Subarray | Sum |
|---|---|---|
0 | [1, 2] | 3 |
1 | [2, 1] | 3 |
2 | [1, 2] | 3 |
3 | [2, 6] | 8 |
4 | [6, 7] | 13 |
5 | [7, 5] | 12 |
6 | [5, 1] | 6 |
Best Left Array
Create:
left_best[i]where left_best[i] is the starting index of the best window among starts 0 through i.
For ties, keep the earlier index to preserve lexicographic order.
That means we update only when the new sum is strictly greater.
if window_sum[i] > window_sum[best]:
best = iBest Right Array
Create:
right_best[i]where right_best[i] is the starting index of the best window among starts i through the end.
For ties, keep the earlier index.
Since we scan from right to left, keeping the earlier index means we update when the new sum is greater than or equal to the current best.
if window_sum[i] >= window_sum[best]:
best = iThe >= is important.
When scanning right to left, index i is earlier than best. If both have the same sum, choosing i gives a lexicographically smaller answer.
Algorithm
- Compute all length-
kwindow sums. - Build
left_best. - Build
right_best. - Try every possible middle start
mid:midranges fromktolen(window_sum) - k - 1- left start is
left_best[mid - k] - right start is
right_best[mid + k]
- Compute total:
window_sum[left] + window_sum[mid] + window_sum[right] - If the total is greater than the best total, update the answer.
- Return the answer.
We update only on strictly greater total. Since we scan mid from left to right and the helper arrays already choose earliest ties, this preserves the lexicographically smallest answer.
Walkthrough
Consider:
nums = [1, 2, 1, 2, 6, 7, 5, 1]
k = 2Window sums:
window_sum = [3, 3, 3, 8, 13, 12, 6]Best left indices:
| i | Best start from 0..i |
|---|---|
| 0 | 0 |
| 1 | 0 |
| 2 | 0 |
| 3 | 3 |
| 4 | 4 |
| 5 | 4 |
| 6 | 4 |
Best right indices:
| i | Best start from i..end |
|---|---|
| 0 | 4 |
| 1 | 4 |
| 2 | 4 |
| 3 | 4 |
| 4 | 4 |
| 5 | 5 |
| 6 | 6 |
Now try middle starts.
For mid = 2:
| Part | Start | Sum |
|---|---|---|
| Left | left_best[0] = 0 | 3 |
| Middle | 2 | 3 |
| Right | right_best[4] = 4 | 13 |
Total:
19For mid = 3:
| Part | Start | Sum |
|---|---|---|
| Left | left_best[1] = 0 | 3 |
| Middle | 3 | 8 |
| Right | right_best[5] = 5 | 12 |
Total:
23This is best, so the answer is:
[0, 3, 5]Correctness
Every valid answer has some middle subarray. Let its start be mid.
Because the three subarrays do not overlap, the left subarray must start at or before mid - k, and the right subarray must start at or after mid + k.
For this fixed mid, the best possible left subarray is exactly left_best[mid - k], because that array stores the maximum-sum valid left window in the allowed range.
The best possible right subarray is exactly right_best[mid + k], because that array stores the maximum-sum valid right window in the allowed range.
Therefore, for each fixed middle index, the algorithm computes the best possible triple using that middle index.
The loop tries every possible middle index, so it considers the best triple for every possible middle subarray.
The maximum over those candidates is therefore the maximum total sum over all valid triples.
For ties, left_best keeps the earliest left index, right_best keeps the earliest right index, and the middle index is scanned from left to right. The answer is updated only when the total sum is strictly greater. Thus, once a maximum-sum triple is found, a later equal-sum triple does not replace its lexicographically smaller indices.
Therefore, the algorithm returns the lexicographically smallest maximum-sum triple.
Complexity
Let n = len(nums).
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | Each array is computed with one linear scan |
| Space | O(n) | We store window sums, left best indices, and right best indices |
Implementation
class Solution:
def maxSumOfThreeSubarrays(self, nums: list[int], k: int) -> list[int]:
n = len(nums)
window_count = n - k + 1
window_sum = [0] * window_count
current = 0
for i, value in enumerate(nums):
current += value
if i >= k:
current -= nums[i - k]
if i >= k - 1:
window_sum[i - k + 1] = current
left_best = [0] * window_count
best = 0
for i in range(window_count):
if window_sum[i] > window_sum[best]:
best = i
left_best[i] = best
right_best = [0] * window_count
best = window_count - 1
for i in range(window_count - 1, -1, -1):
if window_sum[i] >= window_sum[best]:
best = i
right_best[i] = best
answer = [-1, -1, -1]
best_total = -1
for mid in range(k, window_count - k):
left = left_best[mid - k]
right = right_best[mid + k]
total = (
window_sum[left]
+ window_sum[mid]
+ window_sum[right]
)
if total > best_total:
best_total = total
answer = [left, mid, right]
return answerCode Explanation
First, we compute all length-k window sums:
window_count = n - k + 1
window_sum = [0] * window_countThe sliding sum adds the current value:
current += valueOnce the window is too large, it removes the value that fell out:
if i >= k:
current -= nums[i - k]When the first complete window exists, we store its sum:
if i >= k - 1:
window_sum[i - k + 1] = currentNext, left_best[i] stores the best window start from 0 through i.
if window_sum[i] > window_sum[best]:
best = iWe use strict > so equal sums keep the earlier index.
Then, right_best[i] stores the best window start from i through the end.
if window_sum[i] >= window_sum[best]:
best = iWe use >= because we are scanning right to left. On equal sums, the current index i is earlier.
Finally, we try every possible middle window:
for mid in range(k, window_count - k):The left window must end before mid, so it can start no later than mid - k.
The right window must start after the middle window, so it starts at least mid + k.
left = left_best[mid - k]
right = right_best[mid + k]If the total is better, update the answer:
if total > best_total:
best_total = total
answer = [left, mid, right]Testing
def run_tests():
s = Solution()
assert s.maxSumOfThreeSubarrays(
[1, 2, 1, 2, 6, 7, 5, 1],
2,
) == [0, 3, 5]
assert s.maxSumOfThreeSubarrays(
[1, 2, 1, 2, 1, 2, 1, 2, 1],
2,
) == [0, 2, 4]
assert s.maxSumOfThreeSubarrays(
[4, 5, 10, 6, 11, 17, 4, 11, 1, 3],
1,
) == [4, 5, 7]
assert s.maxSumOfThreeSubarrays(
[1, 1, 1, 1, 1, 1],
2,
) == [0, 2, 4]
print("all tests passed")
run_tests()Test meaning:
| Test | Expected | Why |
|---|---|---|
[1,2,1,2,6,7,5,1], k = 2 | [0,3,5] | Standard example |
Repeated [1,2] pattern | [0,2,4] | Tie must choose lexicographically smallest |
k = 1 case | [4,5,7] | Chooses three largest non-overlapping single elements |
| All equal values | [0,2,4] | Tie behavior must stay earliest |