Facebook Pixel

1031. Maximum Sum of Two Non-Overlapping Subarrays

Problem Description

You are given an integer array nums and two integers firstLen and secondLen. Your task is to find two non-overlapping subarrays from nums - one with length firstLen and another with length secondLen - such that the sum of elements in both subarrays is maximized.

Key requirements:

  • Both subarrays must be contiguous (elements must be consecutive in the original array)
  • The two subarrays cannot overlap (they cannot share any elements)
  • The first subarray (with length firstLen) can appear either before or after the second subarray (with length secondLen) in the array
  • You need to return the maximum possible sum of elements from both subarrays combined

For example, if nums = [0,6,5,2,2,5,1,9,4], firstLen = 1, and secondLen = 2:

  • You could choose subarray [9] (length 1, sum = 9) and subarray [6,5] (length 2, sum = 11)
  • The total sum would be 9 + 11 = 20
  • This would be the maximum possible sum for two non-overlapping subarrays with the given lengths

The solution uses a sliding window approach with prefix sums to efficiently calculate subarray sums. It considers two scenarios: placing the firstLen subarray before the secondLen subarray, and placing the secondLen subarray before the firstLen subarray. For each position, it tracks the maximum sum of the first subarray seen so far and combines it with the current second subarray to find the overall maximum sum.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that for two non-overlapping subarrays, one must come before the other. This means we have two possible arrangements:

  1. A subarray of length firstLen followed by a subarray of length secondLen
  2. A subarray of length secondLen followed by a subarray of length firstLen

To find the maximum sum efficiently, we need to avoid checking all possible pairs of subarrays, which would be O(n²). Instead, we can use a sliding window technique combined with dynamic tracking.

The clever observation is: as we slide through the array looking for the second subarray, we can keep track of the best first subarray we've seen so far. At each position i, we ask:

  • "What's the best first subarray that ends before position i?"
  • "What's the sum of the second subarray starting at position i?"
  • The sum of these two gives us a candidate for our answer

To calculate subarray sums quickly, we use prefix sums. With prefix sum array s, the sum of elements from index i to j is simply s[j+1] - s[i].

The algorithm works in two passes:

  1. First pass: Place firstLen subarray first. As we move right, we track the maximum sum of any firstLen subarray seen so far, then check if combining it with the current secondLen subarray gives a better total.

  2. Second pass: Place secondLen subarray first. We do the same process but with the roles reversed.

The variable t maintains the running maximum of the first subarray encountered, ensuring we always pair the current second subarray with the best possible first subarray that doesn't overlap with it. This reduces our time complexity from O(n²) to O(n).

Learn more about Dynamic Programming and Sliding Window patterns.

Solution Approach

The implementation uses prefix sums and a two-pass sliding window approach to find the maximum sum of two non-overlapping subarrays.

Step 1: Build Prefix Sum Array

s = list(accumulate(nums, initial=0))

We create a prefix sum array s where s[i] represents the sum of all elements from index 0 to i-1. This allows us to calculate any subarray sum in O(1) time: the sum from index i to j is s[j+1] - s[i].

Step 2: First Pass - firstLen before secondLen

ans = t = 0
i = firstLen
while i + secondLen - 1 < n:
    t = max(t, s[i] - s[i - firstLen])
    ans = max(ans, t + s[i + secondLen] - s[i])
    i += 1
  • Start at position i = firstLen (the earliest position where we can place the second subarray)
  • t tracks the maximum sum of any firstLen subarray ending at or before position i-1
  • At each position i:
    • Update t with the sum of the firstLen subarray ending at position i-1: s[i] - s[i - firstLen]
    • Calculate the sum of the secondLen subarray starting at position i: s[i + secondLen] - s[i]
    • Update ans with the maximum of current answer and t + current_secondLen_sum
  • Continue until we can't fit a secondLen subarray anymore

Step 3: Second Pass - secondLen before firstLen

t = 0
i = secondLen
while i + firstLen - 1 < n:
    t = max(t, s[i] - s[i - secondLen])
    ans = max(ans, t + s[i + firstLen] - s[i])
    i += 1

This pass mirrors the first pass but with reversed roles:

  • Start at position i = secondLen
  • t now tracks the maximum sum of any secondLen subarray ending at or before position i-1
  • Calculate the sum of the firstLen subarray starting at position i
  • Update the answer accordingly

Why This Works:

  • By maintaining the running maximum (t) of the first subarray, we ensure that at each position, we're pairing the current second subarray with the best possible first subarray that doesn't overlap
  • The two passes cover both possible arrangements of the subarrays
  • Time complexity is O(n) as we make two linear passes through the array
  • Space complexity is O(n) for the prefix sum array

The final answer is the maximum sum found across both arrangements.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example to illustrate the solution approach.

Example: nums = [3, 1, 2, 4, 5], firstLen = 2, secondLen = 1

Step 1: Build Prefix Sum Array

  • nums = [3, 1, 2, 4, 5]
  • s = [0, 3, 4, 6, 10, 15]
    • s[0] = 0 (initial value)
    • s[1] = 0 + 3 = 3
    • s[2] = 3 + 1 = 4
    • s[3] = 4 + 2 = 6
    • s[4] = 6 + 4 = 10
    • s[5] = 10 + 5 = 15

Step 2: First Pass - firstLen (2) before secondLen (1)

Initialize: ans = 0, t = 0, i = 2 (start at firstLen)

  • i = 2:

    • Calculate firstLen subarray ending at i-1: [3,1], sum = s[2] - s[0] = 4 - 0 = 4
    • Update t = max(0, 4) = 4
    • Calculate secondLen subarray starting at i: [2], sum = s[3] - s[2] = 6 - 4 = 2
    • Update ans = max(0, 4 + 2) = 6 (subarrays: [3,1] and [2])
  • i = 3:

    • Calculate firstLen subarray ending at i-1: [1,2], sum = s[3] - s[1] = 6 - 3 = 3
    • Update t = max(4, 3) = 4 (keep the better firstLen subarray [3,1])
    • Calculate secondLen subarray starting at i: [4], sum = s[4] - s[3] = 10 - 6 = 4
    • Update ans = max(6, 4 + 4) = 8 (subarrays: [3,1] and [4])
  • i = 4:

    • Calculate firstLen subarray ending at i-1: [2,4], sum = s[4] - s[2] = 10 - 4 = 6
    • Update t = max(4, 6) = 6
    • Calculate secondLen subarray starting at i: [5], sum = s[5] - s[4] = 15 - 10 = 5
    • Update ans = max(8, 6 + 5) = 11 (subarrays: [2,4] and [5])

Step 3: Second Pass - secondLen (1) before firstLen (2)

Reset: t = 0, i = 1 (start at secondLen)

  • i = 1:

    • Calculate secondLen subarray ending at i-1: [3], sum = s[1] - s[0] = 3 - 0 = 3
    • Update t = max(0, 3) = 3
    • Calculate firstLen subarray starting at i: [1,2], sum = s[3] - s[1] = 6 - 3 = 3
    • Update ans = max(11, 3 + 3) = 11 (no improvement)
  • i = 2:

    • Calculate secondLen subarray ending at i-1: [1], sum = s[2] - s[1] = 4 - 3 = 1
    • Update t = max(3, 1) = 3 (keep the better secondLen subarray [3])
    • Calculate firstLen subarray starting at i: [2,4], sum = s[4] - s[2] = 10 - 4 = 6
    • Update ans = max(11, 3 + 6) = 11 (no improvement)
  • i = 3:

    • Calculate secondLen subarray ending at i-1: [2], sum = s[3] - s[2] = 6 - 4 = 2
    • Update t = max(3, 2) = 3
    • Calculate firstLen subarray starting at i: [4,5], sum = s[5] - s[3] = 15 - 6 = 9
    • Update ans = max(11, 3 + 9) = 12 (subarrays: [3] and [4,5])

Final Answer: 12

The maximum sum is achieved with subarrays [3] (sum = 3) and [4,5] (sum = 9), giving a total of 12.

Solution Implementation

1class Solution:
2    def maxSumTwoNoOverlap(self, nums: List[int], firstLen: int, secondLen: int) -> int:
3        n = len(nums)
4        # Build prefix sum array for O(1) range sum queries
5        # prefix_sum[i] = sum of nums[0:i]
6        prefix_sum = list(accumulate(nums, initial=0))
7      
8        max_total_sum = 0
9      
10        # Case 1: First array comes before second array
11        max_first_sum = 0
12        # Iterate through possible ending positions for the second array
13        for end_of_second in range(firstLen + secondLen, n + 1):
14            # Update max sum of first array that can fit before current position
15            start_of_second = end_of_second - secondLen
16            end_of_first = start_of_second
17            start_of_first = end_of_first - firstLen
18            current_first_sum = prefix_sum[end_of_first] - prefix_sum[start_of_first]
19            max_first_sum = max(max_first_sum, current_first_sum)
20          
21            # Calculate sum of second array at current position
22            current_second_sum = prefix_sum[end_of_second] - prefix_sum[start_of_second]
23          
24            # Update maximum total sum
25            max_total_sum = max(max_total_sum, max_first_sum + current_second_sum)
26      
27        # Case 2: Second array comes before first array
28        max_second_sum = 0
29        # Iterate through possible ending positions for the first array
30        for end_of_first in range(secondLen + firstLen, n + 1):
31            # Update max sum of second array that can fit before current position
32            start_of_first = end_of_first - firstLen
33            end_of_second = start_of_first
34            start_of_second = end_of_second - secondLen
35            current_second_sum = prefix_sum[end_of_second] - prefix_sum[start_of_second]
36            max_second_sum = max(max_second_sum, current_second_sum)
37          
38            # Calculate sum of first array at current position
39            current_first_sum = prefix_sum[end_of_first] - prefix_sum[start_of_first]
40          
41            # Update maximum total sum
42            max_total_sum = max(max_total_sum, max_second_sum + current_first_sum)
43      
44        return max_total_sum
45
1class Solution {
2    public int maxSumTwoNoOverlap(int[] nums, int firstLen, int secondLen) {
3        int n = nums.length;
4      
5        // Build prefix sum array where prefixSum[i] = sum of nums[0...i-1]
6        int[] prefixSum = new int[n + 1];
7        for (int i = 0; i < n; i++) {
8            prefixSum[i + 1] = prefixSum[i] + nums[i];
9        }
10      
11        int maxSum = 0;
12      
13        // Case 1: First subarray comes before second subarray
14        // Iterate through possible positions for the second subarray
15        for (int i = firstLen, maxFirstSum = 0; i + secondLen - 1 < n; i++) {
16            // Track the maximum sum of first subarray ending before position i
17            maxFirstSum = Math.max(maxFirstSum, prefixSum[i] - prefixSum[i - firstLen]);
18          
19            // Calculate sum of second subarray starting at position i
20            int secondSum = prefixSum[i + secondLen] - prefixSum[i];
21          
22            // Update maximum total sum
23            maxSum = Math.max(maxSum, maxFirstSum + secondSum);
24        }
25      
26        // Case 2: Second subarray comes before first subarray
27        // Iterate through possible positions for the first subarray
28        for (int i = secondLen, maxSecondSum = 0; i + firstLen - 1 < n; i++) {
29            // Track the maximum sum of second subarray ending before position i
30            maxSecondSum = Math.max(maxSecondSum, prefixSum[i] - prefixSum[i - secondLen]);
31          
32            // Calculate sum of first subarray starting at position i
33            int firstSum = prefixSum[i + firstLen] - prefixSum[i];
34          
35            // Update maximum total sum
36            maxSum = Math.max(maxSum, maxSecondSum + firstSum);
37        }
38      
39        return maxSum;
40    }
41}
42
1class Solution {
2public:
3    int maxSumTwoNoOverlap(vector<int>& nums, int firstLen, int secondLen) {
4        int n = nums.size();
5      
6        // Build prefix sum array where prefixSum[i] = sum of nums[0..i-1]
7        vector<int> prefixSum(n + 1, 0);
8        for (int i = 0; i < n; ++i) {
9            prefixSum[i + 1] = prefixSum[i] + nums[i];
10        }
11      
12        int maxSum = 0;
13      
14        // Case 1: First subarray comes before second subarray
15        // Iterate through possible positions for the second subarray
16        int maxFirstSubarraySum = 0;
17        for (int secondStart = firstLen; secondStart + secondLen - 1 < n; ++secondStart) {
18            // Track the maximum sum of first subarray ending before current position
19            int currentFirstSum = prefixSum[secondStart] - prefixSum[secondStart - firstLen];
20            maxFirstSubarraySum = max(maxFirstSubarraySum, currentFirstSum);
21          
22            // Calculate sum of second subarray at current position
23            int secondSubarraySum = prefixSum[secondStart + secondLen] - prefixSum[secondStart];
24          
25            // Update maximum total sum
26            maxSum = max(maxSum, maxFirstSubarraySum + secondSubarraySum);
27        }
28      
29        // Case 2: Second subarray comes before first subarray
30        // Iterate through possible positions for the first subarray
31        int maxSecondSubarraySum = 0;
32        for (int firstStart = secondLen; firstStart + firstLen - 1 < n; ++firstStart) {
33            // Track the maximum sum of second subarray ending before current position
34            int currentSecondSum = prefixSum[firstStart] - prefixSum[firstStart - secondLen];
35            maxSecondSubarraySum = max(maxSecondSubarraySum, currentSecondSum);
36          
37            // Calculate sum of first subarray at current position
38            int firstSubarraySum = prefixSum[firstStart + firstLen] - prefixSum[firstStart];
39          
40            // Update maximum total sum
41            maxSum = max(maxSum, maxSecondSubarraySum + firstSubarraySum);
42        }
43      
44        return maxSum;
45    }
46};
47
1function maxSumTwoNoOverlap(nums: number[], firstLen: number, secondLen: number): number {
2    const n = nums.length;
3  
4    // Build prefix sum array where prefixSum[i] = sum of nums[0..i-1]
5    const prefixSum: number[] = new Array(n + 1).fill(0);
6    for (let i = 0; i < n; i++) {
7        prefixSum[i + 1] = prefixSum[i] + nums[i];
8    }
9  
10    let maxSum = 0;
11  
12    // Case 1: First subarray comes before second subarray
13    // Iterate through possible positions for the second subarray
14    let maxFirstSubarraySum = 0;
15    for (let secondStart = firstLen; secondStart + secondLen - 1 < n; secondStart++) {
16        // Track the maximum sum of first subarray ending before current position
17        const currentFirstSum = prefixSum[secondStart] - prefixSum[secondStart - firstLen];
18        maxFirstSubarraySum = Math.max(maxFirstSubarraySum, currentFirstSum);
19      
20        // Calculate sum of second subarray at current position
21        const secondSubarraySum = prefixSum[secondStart + secondLen] - prefixSum[secondStart];
22      
23        // Update maximum total sum
24        maxSum = Math.max(maxSum, maxFirstSubarraySum + secondSubarraySum);
25    }
26  
27    // Case 2: Second subarray comes before first subarray
28    // Iterate through possible positions for the first subarray
29    let maxSecondSubarraySum = 0;
30    for (let firstStart = secondLen; firstStart + firstLen - 1 < n; firstStart++) {
31        // Track the maximum sum of second subarray ending before current position
32        const currentSecondSum = prefixSum[firstStart] - prefixSum[firstStart - secondLen];
33        maxSecondSubarraySum = Math.max(maxSecondSubarraySum, currentSecondSum);
34      
35        // Calculate sum of first subarray at current position
36        const firstSubarraySum = prefixSum[firstStart + firstLen] - prefixSum[firstStart];
37      
38        // Update maximum total sum
39        maxSum = Math.max(maxSum, maxSecondSubarraySum + firstSubarraySum);
40    }
41  
42    return maxSum;
43}
44

Time and Space Complexity

Time Complexity: O(n) where n is the length of the input array nums.

The algorithm consists of:

  • Creating the prefix sum array using accumulate: O(n)
  • First while loop iterates from firstLen to n - secondLen, which is at most n iterations: O(n)
  • Second while loop iterates from secondLen to n - firstLen, which is at most n iterations: O(n)
  • Inside each loop, all operations (max comparisons, array access, arithmetic) are O(1)

Total time complexity: O(n) + O(n) + O(n) = O(n)

Space Complexity: O(n)

The algorithm uses:

  • Prefix sum array s of size n + 1: O(n)
  • A few constant variables (ans, t, i, n): O(1)

Total space complexity: O(n) + O(1) = O(n)

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Off-by-One Errors in Index Calculations

One of the most common mistakes is incorrectly calculating the start and end indices for subarrays, especially when working with prefix sums.

Pitfall Example:

# INCORRECT: Confusing array indices with prefix sum indices
for i in range(firstLen, n):
    first_sum = prefix_sum[i] - prefix_sum[i - firstLen]  # Wrong!
    # This actually gives sum from i-firstLen to i-1, not ending at i

Solution: Always remember that with prefix sums:

  • prefix_sum[i] contains the sum of elements from index 0 to i-1
  • To get sum from index start to end (inclusive), use: prefix_sum[end + 1] - prefix_sum[start]

2. Not Considering Both Arrangements

A critical mistake is only checking one arrangement (e.g., first array before second array) and forgetting that the arrays can be in either order.

Pitfall Example:

# INCORRECT: Only checking one arrangement
def maxSumTwoNoOverlap(self, nums, firstLen, secondLen):
    # Only considers firstLen array before secondLen array
    for i in range(firstLen, len(nums) - secondLen + 1):
        # ... calculate sums ...
    return result  # Missing half the possible solutions!

Solution: Always check both arrangements:

  1. First array of length firstLen before second array of length secondLen
  2. Second array of length secondLen before first array of length firstLen

3. Incorrect Loop Boundaries

Setting wrong loop boundaries can cause index out of bounds errors or miss valid subarrays.

Pitfall Example:

# INCORRECT: Loop boundary doesn't ensure enough space for second array
for i in range(firstLen, n):  # Wrong!
    second_sum = prefix_sum[i + secondLen] - prefix_sum[i]  # May go out of bounds

Solution: Ensure your loop boundaries leave enough space for both arrays:

# CORRECT: Ensure there's room for secondLen array after position i
for i in range(firstLen, n - secondLen + 1):
    second_sum = prefix_sum[i + secondLen] - prefix_sum[i]

4. Not Tracking Maximum of Previous Subarrays

A subtle mistake is recalculating the maximum of all previous subarrays at each step instead of maintaining a running maximum.

Pitfall Example:

# INEFFICIENT: Recalculating max every time (O(n²) complexity)
for i in range(firstLen + secondLen, n + 1):
    max_first = 0
    # Recalculating max of all previous firstLen subarrays
    for j in range(0, i - secondLen - firstLen + 1):
        first_sum = prefix_sum[j + firstLen] - prefix_sum[j]
        max_first = max(max_first, first_sum)
    # ... rest of logic

Solution: Maintain a running maximum that gets updated incrementally:

# CORRECT: Maintain running maximum (O(n) complexity)
max_first_sum = 0
for i in range(firstLen + secondLen, n + 1):
    # Only check the new subarray that becomes available
    new_first_sum = prefix_sum[i - secondLen] - prefix_sum[i - secondLen - firstLen]
    max_first_sum = max(max_first_sum, new_first_sum)
    # ... rest of logic

5. Forgetting Edge Cases

Not handling edge cases like when the array length equals firstLen + secondLen.

Solution: Ensure your solution handles:

  • Arrays where len(nums) == firstLen + secondLen (only one valid configuration)
  • Arrays with negative numbers (prefix sums can decrease)
  • Cases where firstLen == secondLen (both arrangements might give the same result, but still need to check both)
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following is a good use case for backtracking?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More