https://leetcode.com/problems/maximum-good-subarray-sum/description/
First, I thought this is intuitive and this would be the optimal solution
class Solution {
public:
long long maximumSubarraySum(vector<int>& nums, int k) {
int n = nums.size();
long long max_sum = LLONG_MIN;
bool found = false;
for(int i=0;i<n;i++){
long long currSum = nums[i];
for(int j=i+1;j<n;j++){
currSum+=nums[j];
if(abs(nums[i] - nums[j])==k){
max_sum = max(currSum,max_sum);
found=true;
}
}
}
return found ? max_sum : 0;
}
};
However the above code gives TLE - only 777 / 782 test cases passed as it is O(n^2)
Thus we have to think of an optimised solution
If |nums[i] - nums[j]| == k
, then either:
nums[i] = nums[j] + k
, ornums[i] = nums[j] - k
Strategy: For each ending position j, check if we’ve seen the required starting values before!
class Solution {
public:
long long maximumSubarraySum(vector<int>& nums, int k) {
int n = nums.size();
vector<long long> prefixSum(n + 1, 0);
// Build prefix sum array
for(int i = 0; i < n; i++) {
prefixSum[i + 1] = prefixSum[i] + nums[i];
}
// Map: value -> minimum prefix sum seen so far for that value
unordered_map<int, long long> minPrefixForValue;
long long maxSum = LLONG_MIN;
bool found = false;
for(int j = 0; j < n; j++) {
// Check if we can form a good subarray ending at j
int target1 = nums[j] + k; // nums[i] should be nums[j] + k
int target2 = nums[j] - k; // nums[i] should be nums[j] - k
// Check both possible starting values
if(minPrefixForValue.count(target1)) {
// prefixSum[i] -> get from the hashmap
//prefixSum[j+1] - prefixSum[i]
long long subarraySum = prefixSum[j + 1] - minPrefixForValue[target1];
maxSum = max(maxSum, subarraySum);
found = true;
}
if(minPrefixForValue.count(target2)) {
//prefixSum[j+1] - prefixSum[i]
long long subarraySum = prefixSum[j + 1] - minPrefixForValue[target2];
maxSum = max(maxSum, subarraySum);
found = true;
}
// Update minimum prefix sum for current value
if(!minPrefixForValue.count(nums[j]) || prefixSum[j] < minPrefixForValue[nums[j]]) {
minPrefixForValue[nums[j]] = prefixSum[j];
}
}
return found ? maxSum : 0;
}
};
Minimize Starting Prefix: To maximize subarray sum, we store the minimum prefix sum for each value (since we subtract it)
🎯 The Condition Breakdown
if(!minPrefixForValue.count(nums[j]) || prefixSum[j] < minPrefixForValue[nums[j]]) {
minPrefixForValue[nums[j]] = prefixSum[j];
}
This has two parts:
!minPrefixForValue.count(nums[j])
→ Haven’t seen this value beforeprefixSum[j] < minPrefixForValue[nums[j]]
→ Current prefix is smaller than stored one
🧠 Why Store the MINIMUM Prefix Sum?
Key Insight: To maximize subarray sum, we want to minimize what we subtract!
Subarray sum formula: prefixSum[j+1] - prefixSum[i]
To maximize this, make prefixSum[i]
as small as possible.
thus we store the minimum prefix sum for that value as we iterate
if(!minPrefixForValue.count(nums[j]) || prefixSum[j] < minPrefixForValue[nums[j]]) {
minPrefixForValue[nums[j]] = prefixSum[j];
}