Taro Logo

Maximum Sum of Two Non-Overlapping Subarrays

Medium
Amazon logo
Amazon
Topics:
ArraysSliding WindowsDynamic Programming

Given an integer array nums and two integers firstLen and secondLen, return the maximum sum of elements in two non-overlapping subarrays with lengths firstLen and secondLen.

The array with length firstLen could occur before or after the array with length secondLen, but they have to be non-overlapping.

A subarray is a contiguous part of an array.

For example:

  1. nums = [0,6,5,2,2,5,1,9,4], firstLen = 1, secondLen = 2. The expected output is 20 because one choice of subarrays is [9] with length 1, and [6,5] with length 2. 9 + 6 + 5 = 20.
  2. nums = [3,8,1,3,2,1,8,9,0], firstLen = 3, secondLen = 2. The expected output is 29 because one choice of subarrays is [3,8,1] with length 3, and [8,9] with length 2. 3 + 8 + 1 + 8 + 9 = 29.
  3. nums = [2,1,5,6,0,9,5,0,3,8], firstLen = 4, secondLen = 3. The expected output is 31 because one choice of subarrays is [5,6,0,9] with length 4, and [0,3,8] with length 3. 5 + 6 + 0 + 9 + 0 + 3 + 8 = 31.

What is the most efficient way to find the maximum sum, and what is the time and space complexity of your solution?

Solution


Naive Solution

A brute-force approach involves iterating through all possible subarrays of length firstLen and secondLen and checking if they are non-overlapping. We calculate the sum of each valid pair of subarrays and keep track of the maximum sum encountered.

Code (Python)

def max_sum_two_non_overlapping_subarrays_brute_force(nums, firstLen, secondLen):
    max_sum = 0
    n = len(nums)

    for i in range(n - firstLen + 1):
        for j in range(n - secondLen + 1):
            # Check if subarrays are non-overlapping
            if i + firstLen <= j or j + secondLen <= i:
                current_sum = sum(nums[i:i+firstLen]) + sum(nums[j:j+secondLen])
                max_sum = max(max_sum, current_sum)

    return max_sum

Time Complexity

O(N^2), where N is the length of the input array nums, because of the nested loops.

Space Complexity

O(1), as we only use a few variables to store the maximum sum and loop indices.

Optimal Solution

A more efficient approach uses prefix sums to calculate subarray sums in O(1) time. We can iterate through all possible split points and consider two cases: firstLen subarray comes before secondLen subarray and vice versa.

Algorithm

  1. Calculate the prefix sum array to allow O(1) calculation of subarray sums.
  2. Iterate through all possible starting positions for the firstLen subarray.
  3. For each firstLen subarray, find the maximum sum of a secondLen subarray that doesn't overlap.
  4. Repeat steps 2 and 3, but with the roles of firstLen and secondLen swapped.
  5. Return the maximum sum found.

Code (Python)

def max_sum_two_non_overlapping_subarrays(nums, firstLen, secondLen):
    n = len(nums)

    # Calculate prefix sums
    prefix_sum = [0] * (n + 1)
    for i in range(1, n + 1):
        prefix_sum[i] = prefix_sum[i - 1] + nums[i - 1]

    def calculate_max_sum(firstLen, secondLen):
        max_sum = 0
        for i in range(firstLen, n - secondLen + 1):
            first_subarray_sum = prefix_sum[i] - prefix_sum[i - firstLen]
            
            # Find max second subarray sum before first subarray
            max_second_before = 0
            for k in range(secondLen, i - firstLen + 1):
                max_second_before = max(max_second_before, prefix_sum[i-firstLen] - prefix_sum[i-firstLen-secondLen])

            # Find max second subarray sum after first subarray
            max_second_after = 0
            for j in range(i + secondLen, n + 1):
                max_second_after = max(max_second_after, prefix_sum[j] - prefix_sum[j - secondLen])
            
            max_sum = max(max_sum, first_subarray_sum + max(max_second_before, max_second_after))
        
        return max_sum

    return max(calculate_max_sum(firstLen, secondLen), calculate_max_sum(secondLen, firstLen))

Optimized Code (Python)

def max_sum_two_non_overlapping_subarrays_optimized(nums, firstLen, secondLen):
    n = len(nums)
    prefix_sum = [0] * (n + 1)
    for i in range(1, n + 1):
        prefix_sum[i] = prefix_sum[i - 1] + nums[i - 1]

    def calculate_max_sum(firstLen, secondLen):
        max_sum = 0
        max_second_before = 0
        for i in range(firstLen + secondLen, n + 1):
            max_second_before = max(max_second_before, prefix_sum[i - firstLen] - prefix_sum[i - firstLen - secondLen])
            first_subarray_sum = prefix_sum[i] - prefix_sum[i - firstLen]
            max_sum = max(max_sum, first_subarray_sum + max_second_before)
        return max_sum

    return max(calculate_max_sum(firstLen, secondLen), calculate_max_sum(secondLen, firstLen))

Time Complexity

O(N), where N is the length of the input array nums. This is because we iterate through the array a constant number of times.

Space Complexity

O(N), for storing the prefix sum array.

Edge Cases

  • If nums is empty or firstLen + secondLen > len(nums), return 0 (or handle the error appropriately).
  • If firstLen or secondLen is 0 or negative, raise an exception or return an appropriate error value.
  • If all elements in nums are negative, the function should still return the maximum possible sum (which may be negative).