Loading problems...
TL;DR: Iterate through the array maintaining a running prefix sum and a hash map that stores the minimum prefix sum seen before each unique value; for each element, check if a valid start element exists in the map and update the maximum sum.
The problem asks us to find the maximum sum of a "good" subarray within an array nums. A subarray is defined as "good" if the absolute difference between its first element and its last element is exactly k. If multiple such subarrays exist, we must return the one with the largest sum. If no such subarray exists, we return 0.
This is a classic optimization problem found in LeetCode 3026: Maximum Good Subarray Sum, where we must efficiently query past states to maximize a current range sum.
The naive approach involves iterating through every possible subarray, checking if it is "good," and if so, calculating its sum to compare with the global maximum.
i and end index j of the subarray.|nums[i] - nums[j]| == k.nums[i] to nums[j].Pseudo-code:
textmax_sum = negative_infinity found_valid = false for i from 0 to n-1: current_subarray_sum = 0 for j from i to n-1: current_subarray_sum += nums[j] if abs(nums[i] - nums[j]) == k: max_sum = max(max_sum, current_subarray_sum) found_valid = true return found_valid ? max_sum : 0
Why this fails:
The time complexity of this approach is . Given the constraint nums.length <= 10^5, an solution requires approximately operations, which far exceeds the typical limit of operations per second allowed by LeetCode. This will result in a Time Limit Exceeded (TLE) error.
To optimize the brute force approach, we must avoid recalculating sums repeatedly and avoid searching for the left boundary i linearly.
The sum of a subarray nums[i..j] can be expressed using prefix sums:
We iterate j from to . For the current element nums[j], we need to find a previous index i such that:
This implies nums[i] must be either nums[j] - k or nums[j] + k.
To maximize the total subarray sum, we need to minimize PrefixSum[i-1].
Instead of scanning backwards for i, we can use a Hash Map to store the minimal prefix sum encountered before every unique value x seen so far. As we slide the right boundary j forward, we perform an lookup in our map to see if a valid nums[i] exists and immediately calculate the potential max sum.
Visual Description:
Imagine traversing the array. At each step, you calculate the cumulative sum. You record this cumulative sum in a dictionary keyed by the current number value. If you encounter the number 5, and the cumulative sum just before this 5 was 10, you store {5: 10}. Later, if you encounter a number that needs a 5 to complete the "good" condition, you look up 5 in your dictionary. If the dictionary returns 10, you know that a subarray starting with 5 would subtract 10 from your current total cumulative sum. By storing only the minimum previous cumulative sum for each number, you ensure that any "window" you form is maximal.

max_sum to a very small number (e.g., negative infinity) to handle negative sums.current_sum to 0.min_prefix_map as an empty hash map.nums with index j and value num:
num to current_sum.num - k exists in min_prefix_map:
candidate = current_sum - min_prefix_map[num - k]max_sum = max(max_sum, candidate)num + k exists in min_prefix_map:
candidate = current_sum - min_prefix_map[num + k]max_sum = max(max_sum, candidate)num is current_sum - num.num is not in min_prefix_map OR (current_sum - num) < min_prefix_map[num]:
min_prefix_map[num] = current_sum - num.max_sum is still negative infinity (no good subarray found), return 0. Otherwise, return max_sum.The algorithm relies on the arithmetic property of contiguous subarray sums: .
For a fixed ending position , the sum is maximized when is minimized.
Our map invariant ensures that min_prefix_map[val] always holds the minimum for any index seen so far where nums[i] == val.
Therefore, when we check for nums[j] - k or nums[j] + k, we are guaranteed to calculate the maximum possible sum for a good subarray ending at . Since we iterate through all possible end positions , the global maximum is guaranteed to be found.
cpp#include <vector> #include <unordered_map> #include <algorithm> #include <climits> using namespace std; class Solution { public: long long maximumSubarraySum(vector<int>& nums, int k) { // Use a large negative number as sentinel. // 0 is not safe because valid sums can be negative. long long max_sum = LLONG_MIN; // Map stores: value -> minimum prefix sum strictly before that value unordered_map<int, long long> min_prefix; long long current_sum = 0; for (int num : nums) { // Update running sum (PrefixSum[j]) current_sum += num; // Check for target: num - k if (min_prefix.count(num - k)) { long long current_good_sum = current_sum - min_prefix[num - k]; if (current_good_sum > max_sum) { max_sum = current_good_sum; } } // Check for target: num + k if (min_prefix.count(num + k)) { long long current_good_sum = current_sum - min_prefix[num + k]; if (current_good_sum > max_sum) { max_sum = current_good_sum; } } // Update map with current number. // We store the prefix sum BEFORE adding the current number. long long prefix_before_current = current_sum - num; if (!min_prefix.count(num) || prefix_before_current < min_prefix[num]) { min_prefix[num] = prefix_before_current; } } // If max_sum was never updated, no good subarray exists. return (max_sum == LLONG_MIN) ? 0 : max_sum; } };
javaimport java.util.HashMap; import java.util.Map; class Solution { public long maximumSubarraySum(int[] nums, int k) { // Use Long.MIN_VALUE as sentinel long maxSum = Long.MIN_VALUE; // Map stores: value -> minimum prefix sum strictly before that value Map<Integer, Long> minPrefix = new HashMap<>(); long currentSum = 0; for (int num : nums) { currentSum += num; // Check for targets if (minPrefix.containsKey(num - k)) { maxSum = Math.max(maxSum, currentSum - minPrefix.get(num - k)); } if (minPrefix.containsKey(num + k)) { maxSum = Math.max(maxSum, currentSum - minPrefix.get(num + k)); } // Update map logic long prefixBeforeCurrent = currentSum - num; if (!minPrefix.containsKey(num) || prefixBeforeCurrent < minPrefix.get(num)) { minPrefix.put(num, prefixBeforeCurrent); } } return maxSum == Long.MIN_VALUE ? 0 : maxSum; } }
pythonclass Solution: def maximumSubarraySum(self, nums: List[int], k: int) -> int: # Initialize with infinity to handle negative max sums correctly max_sum = float('-inf') # Map stores: value -> minimum prefix sum strictly before that value min_prefix = {} current_sum = 0 for num in nums: current_sum += num # Check if valid start points exist in map if (num - k) in min_prefix: max_sum = max(max_sum, current_sum - min_prefix[num - k]) if (num + k) in min_prefix: max_sum = max(max_sum, current_sum - min_prefix[num + k]) # The prefix sum before adding the current number prefix_before_current = current_sum - num # Only update map if we found a smaller prefix sum for this value if num not in min_prefix or prefix_before_current < min_prefix[num]: min_prefix[num] = prefix_before_current return 0 if max_sum == float('-inf') else max_sum
Time Complexity:
We iterate through the array nums exactly once. Inside the loop, map lookups (checking for existence and retrieving values) and insertions operate in average time. Thus, the total time complexity is linear with respect to the input size.
Space Complexity:
In the worst case, all elements in nums are unique. The hash map will store an entry for every distinct element in the array, leading to linear space usage.
The Sliding Window - Variable Size (Condition-Based) pattern and the Hash Map optimization technique are highly transferable.
Understanding how to combine iteration (window expansion) with a Hash Map (efficient history lookup) is key to solving these interview questions optimally.
Why does my solution fail on large inputs?
Why is my answer wrong for negative numbers?
max_sum to 0.[-5, -2], , sum is -7). If you initialize to 0, you will incorrectly return 0 instead of -7.Why do I get integer overflow?
int) for the sum accumulator.long long (C++) or long (Java).Why is my map update logic incorrect?
min_prefix_map[num] with the current prefix sum.current_sum - previous_prefix. To do this, we must subtract the smallest possible previous_prefix. If we overwrite a small prefix sum with a larger one just because we encountered the number again later, we lose the optimal starting point.