Taro Logo

Maximum Good Subarray Sum

Medium
Amazon logo
Amazon
1 view
Topics:
Arrays

You are given an array nums of length n and a positive integer k. A subarray of nums is called good if the absolute difference between its first and last element is exactly k, in other words, the subarray nums[i..j] is good if |nums[i] - nums[j]| == k. Return the maximum sum of a good subarray of nums. If there are no good subarrays, return 0.

Example 1:

Input: nums = [1,2,3,4,5,6], k = 1 Output: 11 Explanation: The absolute difference between the first and last element must be 1 for a good subarray. All the good subarrays are: [1,2], [2,3], [3,4], [4,5], and [5,6]. The maximum subarray sum is 11 for the subarray [5,6].

Example 2:

Input: nums = [-1,3,2,4,5], k = 3 Output: 11 Explanation: The absolute difference between the first and last element must be 3 for a good subarray. All the good subarrays are: [-1,3,2], and [2,4,5]. The maximum subarray sum is 11 for the subarray [2,4,5].

Example 3:

Input: nums = [-1,-2,-3,-4], k = 2 Output: -6 Explanation: The absolute difference between the first and last element must be 2 for a good subarray. All the good subarrays are: [-1,-2,-3], and [-2,-3,-4]. The maximum subarray sum is -6 for the subarray [-1,-2,-3].

Constraints:

  • 2 <= nums.length <= 10^5
  • -10^9 <= nums[i] <= 10^9
  • 1 <= k <= 10^9

Solution


Clarifying Questions

When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:

  1. What is the range of integer values within the input array?
  2. Can the input array be empty or null?
  3. If no subarray has a positive sum, what should I return (e.g., 0, null, an empty array)?
  4. By 'subarray', do you mean a contiguous subarray?
  5. Is there a specific criterion for choosing the 'best' subarray if multiple subarrays have the same maximum sum (e.g., shortest length, earliest starting index)?

Brute Force Solution

Approach

The brute force approach to finding the maximum good subarray sum involves checking every possible subarray within the given input. We calculate the sum of each of these subarrays and determine if it is a 'good' subarray, according to the problem's specific criteria. Finally, we compare all of the sums from the 'good' subarrays and select the largest one.

Here's how the algorithm would work step-by-step:

  1. Start with the very first number in the list.
  2. Consider only that number as a subarray and calculate its sum.
  3. Then, consider the first two numbers as a subarray and calculate their sum.
  4. Next, consider the first three numbers, and so on, each time calculating the sum of the growing subarray.
  5. Continue this process until you have considered the entire list as the initial subarray.
  6. Now, start again, but begin with the second number in the list.
  7. Repeat the process of creating progressively larger subarrays, calculating their sums, until you reach the end of the list.
  8. Keep doing this, each time starting from the next number in the list, until you've started from every number.
  9. For each subarray you considered, check if it is a 'good' subarray, based on some predefined set of rules.
  10. If a subarray is 'good', store its sum.
  11. Once you have examined all possible subarrays, compare the sums of all the 'good' subarrays.
  12. The largest of these sums is the answer.

Code Implementation

def maximum_good_subarray_sum_brute_force(numbers):    maximum_sum = float('-inf')
    list_length = len(numbers)
    for start_index in range(list_length):
        for end_index in range(start_index, list_length):
            current_subarray = numbers[start_index:end_index + 1]
            current_sum = sum(current_subarray)
            # Check if the current subarray is considered 'good'
            if is_good_subarray(current_subarray):

                # Update maximum_sum if current_sum is larger
                maximum_sum = max(maximum_sum, current_sum)
    return maximum_sum
def is_good_subarray(subarray):
    if not subarray:
        return False
    # Placeholder function for defining what a 'good' subarray is
    # Replace with your actual 'good' subarray logic
    for number in subarray:
        if number < 0:
            return False
    return True

Big(O) Analysis

Time Complexity
O(n²)The algorithm iterates through all possible subarrays of the input array. For an array of size n, the outer loop iterates n times, and for each iteration of the outer loop, the inner loop iterates up to n times to define a subarray. Therefore, the number of subarray sum calculations and 'good' subarray checks grow proportionally to n * n, making the time complexity O(n²).
Space Complexity
O(1)The described brute force algorithm calculates subarray sums iteratively. It does not create any auxiliary data structures that scale with the input size N. It only uses a few variables to store the current subarray sum and potentially the maximum good subarray sum encountered so far, which requires constant extra space regardless of the size of the input array. Therefore, the auxiliary space complexity is O(1).

Optimal Solution

Approach

The key idea is to keep track of the best possible sum ending at each point in the list. As we go, if the running sum ever dips below zero, we simply restart from zero. This avoids dragging down future sums with negative baggage.

Here's how the algorithm would work step-by-step:

  1. Start at the beginning of the list and keep a running total of the numbers we've seen so far.
  2. If the running total ever becomes negative, reset it to zero. This is because a negative running total will always decrease any future subarray sum.
  3. As we go, remember the largest running total we've encountered. This is the maximum good subarray sum.
  4. Continue to the end of the list, updating the running total and the maximum sum as necessary.
  5. Once we've reached the end, the largest running total we saved is the answer.

Code Implementation

def max_good_subarray_sum(numbers):
    maximum_sum_so_far = 0
    current_sum = 0

    for number in numbers:
        current_sum += number

        # Reset current sum to 0 if it becomes negative.
        if current_sum < 0:
            current_sum = 0

        # Update the maximum sum found so far.
        if current_sum > maximum_sum_so_far:
            maximum_sum_so_far = current_sum

    return maximum_sum_so_far

Big(O) Analysis

Time Complexity
O(n)The algorithm iterates through the input list once, performing a constant amount of work for each element. The running total is updated, and the maximum sum is compared against the current running total in each iteration. Since the number of operations scales linearly with the input size n (the length of the list), the time complexity is O(n).
Space Complexity
O(1)The algorithm uses only two variables: one to keep track of the running total and another to store the maximum sum encountered so far. The amount of memory needed for these variables does not depend on the size of the input list N. Therefore, the auxiliary space complexity is constant.

Edge Cases

CaseHow to Handle
Empty input arrayReturn 0, as there's no subarray to sum.
Array with a single elementReturn the single element if it's positive; otherwise, return 0.
Array with all negative numbersReturn 0, as an empty subarray is better than any negative sum.
Array with all zerosReturn 0, the sum of the entire array.
Array with very large positive numbers causing potential integer overflowUse a data type with a larger range, like long, to store intermediate sums.
Array with alternating large positive and large negative numbersKadane's Algorithm correctly handles fluctuations, maximizing good subarrays.
Maximum-sized array (constrained by memory)Kadane's Algorithm runs in O(n) time and O(1) space, making it efficient even for large arrays.
All numbers are the same positive valueThe algorithm correctly sums the entire array to provide the maximum sum.