Maximum Subarray Sum with One Deletion
MedPrompt
Given an array of integers, return the maximum sum for a non-empty subarray (contiguous elements) with at most one element deletion. In other words, you want to choose a subarray and optionally delete one element from it so that there is still at least one element left and the sum of the remaining elements is maximum possible.
Note that the subarray needs to be non-empty after deleting one element.
Example 1:
Input: arr = [1,-2,0,3]
Output: 4
Explanation: Because we can choose [1, -2, 0, 3] and drop -2, thus the subarray [1, 0, 3] becomes the maximum value.Example 2:
Input: arr = [1,-2,-2,3]
Output: 3
Explanation: We just choose [3] and it's the maximum sum.Example 3:
Input: arr = [-1,-1,-1,-1]
Output: -1
Explanation: The final subarray needs to be non-empty. You can't choose [-1] and delete -1 from it, then get an empty subarray to make the sum equals to 0.
Constraints:
1 <= arr.length <= 105-104 <= arr[i] <= 104
Approaches
3 approaches with complexity analysis and trade-offs.
A brute-force approach involves examining every possible contiguous subarray, and for each subarray, calculating the maximum possible sum by either keeping all its elements or deleting one of them. To make this slightly more efficient, instead of a naive O(N^3) approach, we can use nested loops to define the start and end of a subarray. While iterating to expand the subarray, we can simultaneously calculate its sum and track its minimum element. This allows us to find the sum with and without a deletion in O(1) time for each expansion step, leading to an overall O(N^2) complexity.
Algorithm
- Initialize
max_sumto the first element of the array, as the subarray must be non-empty. - Iterate through the array with an outer loop using index
ifrom0ton-1. This indexiwill represent the starting point of a potential subarray. - Inside the outer loop, start an inner loop with index
jfromiton-1. This indexjwill be the ending point of the subarray. - For each subarray
arr[i...j], we maintain itscurrent_sumand themin_valwithin it. - As the inner loop for
jprogresses, updatecurrent_sumby addingarr[j]andmin_valby takingmin(min_val, arr[j]). - For each subarray
arr[i...j], there are two possibilities for the sum: a. No Deletion: The sum is simplycurrent_sum. We update our globalmax_sumwith this value:max_sum = max(max_sum, current_sum). b. One Deletion: We can delete one element. To maximize the sum, we should delete the smallest element,min_val. This is only possible if the subarray has more than one element (i.e.,j > i). The sum would becurrent_sum - min_val. We update our globalmax_sumwith this value as well:max_sum = max(max_sum, current_sum - min_val). - After both loops complete,
max_sumwill hold the maximum possible subarray sum with at most one deletion.
Walkthrough
This approach iterates through all possible start and end points of a subarray. For each subarray arr[i...j], it calculates two potential maximums: the sum of the subarray as is (no deletion), and the sum of the subarray after removing its smallest element (one deletion). The check j > i ensures that we only consider deletion for subarrays with at least two elements, so they remain non-empty after deletion. The global maximum across all subarrays and both cases (deletion/no deletion) is the answer.
class Solution { public int maximumSum(int[] arr) { int n = arr.length; if (n == 1) { return arr[0]; } int maxSum = arr[0]; for (int i = 0; i < n; i++) { int currentSum = 0; int minVal = Integer.MAX_VALUE; for (int j = i; j < n; j++) { currentSum += arr[j]; minVal = Math.min(minVal, arr[j]); // Case 1: No deletion maxSum = Math.max(maxSum, currentSum); // Case 2: One deletion // We can only delete if the subarray has more than one element if (j > i) { maxSum = Math.max(maxSum, currentSum - minVal); } } } return maxSum; }}Complexity
Time
O(N^2), where N is the length of the array. The nested loops iterate through all possible subarrays, which are O(N^2) in number.
Space
O(1), as we only use a few variables to keep track of the current state regardless of the input size.
Trade-offs
Pros
The logic is straightforward and easy to implement.
It uses constant extra space.
Cons
The
O(N^2)time complexity makes it too slow for large input sizes as specified in the constraints, leading to a 'Time Limit Exceeded' error on most platforms.
A more efficient approach uses dynamic programming. The core idea is that for any element arr[i] we consider deleting, the maximum sum is the sum of the best possible subarray ending just before it (arr[i-1]) and the best possible subarray starting just after it (arr[i+1]).
We can precompute these values. We use a forward pass (left to right) to calculate the maximum subarray sum ending at each index i. Then, we use a backward pass (right to left) to calculate the maximum subarray sum starting at each index i. Finally, we iterate through the array to combine these results and find the overall maximum.
Algorithm
- The problem can be split into two subproblems: finding the maximum subarray sum with no deletions, and finding the maximum subarray sum with one deletion.
- No Deletion Case: This is the classic maximum subarray problem, which can be solved with Kadane's algorithm. We can compute this while calculating our
forwardarray. - One Deletion Case: If we delete the element
arr[i], the resulting subarray is formed by a subarray to the left ofiand a subarray to the right ofi. To maximize the sum, we should take the maximum subarray sum ending ati-1and add it to the maximum subarray sum starting ati+1. - Algorithm Steps:
a. Create an array
forwardof sizen.forward[i]will store the maximum subarray sum ending at indexi. Populate it by iterating from left to right. b. While fillingforward, keep track of the overall maximum sum found so far. This will be ourmax_sum_no_deletion. c. Create an arraybackwardof sizen.backward[i]will store the maximum subarray sum starting at indexi. Populate it by iterating from right to left. d. Initializeresult = max_sum_no_deletion. e. Iterate fromi = 1ton-2. For eachi, consider deletingarr[i]. The sum would beforward[i-1] + backward[i+1]. Updateresult = max(result, forward[i-1] + backward[i+1]). f. Returnresult.
Walkthrough
This method involves three main passes through the array.
First Pass (Forward): We apply Kadane's algorithm from left to right. We build a forward array where forward[i] holds the maximum sum of a contiguous subarray ending at index i. We also find the maximum value in this forward array, which represents the maximum subarray sum without any deletions.
Second Pass (Backward): We apply Kadane's algorithm from right to left. We build a backward array where backward[i] holds the maximum sum of a contiguous subarray starting at index i.
Third Pass (Combine): We iterate through the array from index 1 to n-2. For each index i, we calculate forward[i-1] + backward[i+1]. This value represents the maximum sum we can get if we choose a subarray that spans across i and delete arr[i]. We compare this with the maximum sum found so far (which was initialized with the no-deletion max sum) and update it if necessary.
class Solution { public int maximumSum(int[] arr) { int n = arr.length; if (n == 1) { return arr[0]; } int[] forward = new int[n]; int[] backward = new int[n]; // Forward pass (Kadane's from left) int currentMax = arr[0]; forward[0] = arr[0]; int maxSumNoDeletion = arr[0]; for (int i = 1; i < n; i++) { currentMax = Math.max(arr[i], currentMax + arr[i]); forward[i] = currentMax; maxSumNoDeletion = Math.max(maxSumNoDeletion, forward[i]); } // Backward pass (Kadane's from right) currentMax = arr[n - 1]; backward[n - 1] = arr[n - 1]; for (int i = n - 2; i >= 0; i--) { currentMax = Math.max(arr[i], currentMax + arr[i]); backward[i] = currentMax; } // Combine results int maxSumWithDeletion = Integer.MIN_VALUE; for (int i = 1; i < n - 1; i++) { maxSumWithDeletion = Math.max(maxSumWithDeletion, forward[i - 1] + backward[i + 1]); } return Math.max(maxSumNoDeletion, maxSumWithDeletion); }}Complexity
Time
O(N), as we perform three separate passes over the array, each taking linear time. O(N) + O(N) + O(N) = O(N).
Space
O(N), where N is the length of the array. We use two additional arrays, `forward` and `backward`, each of size N.
Trade-offs
Pros
Achieves optimal linear time complexity.
The logic is a clear extension of the standard Kadane's algorithm.
Cons
Requires O(N) extra space for the two arrays, which can be significant for very large inputs.
The most optimal solution builds upon the dynamic programming concept but reduces the space complexity to O(1). We can achieve this by realizing that to calculate the state for the current index i, we only need the state from the immediate previous index i-1. This allows us to use a few variables to keep track of the necessary information instead of entire arrays.
We iterate through the array once, maintaining two key values:
no_del: The maximum subarray sum ending at the current position with zero deletions.one_del: The maximum subarray sum considering elements up to the current position with one deletion.
The final answer is the maximum value encountered among these two states throughout the iteration.
Algorithm
- Initialize three variables:
max_sum(the global answer),no_del(max subarray sum ending at current index with no deletion), andone_del(max subarray sum with one deletion considering elements up to current index). Initialize all toarr[0]. - Iterate through the array from the second element (
i = 1ton-1). - In each iteration, we need to update
one_delandno_del. The order is important. - First, update
one_del. A subarray with one deletion up to indexican be formed in two ways: a. Take a subarray with one deletion that was formed up to indexi-1, and extend it witharr[i]. The sum isone_del_previous + arr[i]. b. Take a subarray with no deletions ending ati-1, and delete the current elementarr[i]. The sum is simplyno_del_previous. So, the newone_delismax(one_del + arr[i], no_del). - Next, update
no_del. This is the standard Kadane's algorithm update: a. Extend the previousno_delsubarray witharr[i]:no_del + arr[i]. b. Start a new subarray with justarr[i]. So, the newno_delismax(no_del + arr[i], arr[i]). - After updating both
one_delandno_del, update the globalmax_sumby taking the maximum ofmax_sum, the newone_del, and the newno_del. - After the loop,
max_sumholds the final answer.
Walkthrough
This approach uses a single pass and constant extra space. We define two DP states that we update at each step i:
no_del: This tracks the maximum sum of a subarray ending atiwithout any deletions. Its transition isno_del = max(arr[i], no_del + arr[i]), which is the standard Kadane's recurrence.one_del: This tracks the maximum sum of a subarray with one deletion, considering all elements up toi. Its transition isone_del = max(one_del + arr[i], no_del_previous). This means we either extend a previously formed one-deletion subarray, or we form a new one-deletion subarray by taking a no-deletion subarray ending ati-1and 'deleting'arr[i].
We must be careful to use the no_del value from the previous step (i-1) when updating one_del for the current step i. We keep a running max_sum of all no_del and one_del values computed.
class Solution { public int maximumSum(int[] arr) { int n = arr.length; if (n == 1) { return arr[0]; } // max subarray sum ending at current index with 0 deletions int no_del = arr[0]; // max subarray sum ending at current index with 1 deletion int one_del = arr[0]; // global max sum int maxSum = arr[0]; for (int i = 1; i < n; i++) { // To calculate one_del for index i, we need no_del from index i-1. // So we calculate the new one_del before updating no_del. // one_del can be: // 1. extending previous one_del subarray: one_del + arr[i] // 2. deleting arr[i] from a no_del subarray: no_del (from previous step) one_del = Math.max(one_del + arr[i], no_del); // Standard Kadane's for no_del no_del = Math.max(no_del + arr[i], arr[i]); maxSum = Math.max(maxSum, Math.max(one_del, no_del)); } return maxSum; }}Complexity
Time
O(N), as we iterate through the input array just once.
Space
O(1), as we only use a fixed number of variables to store the DP states.
Trade-offs
Pros
Achieves optimal O(N) time complexity.
Achieves optimal O(1) space complexity.
Requires only a single pass through the array.
Cons
The logic for the state transitions, especially for the
one_delvariable, can be less intuitive to grasp compared to the two-pass approach.
Solutions
Solution
class Solution {public int maximumSum(int[] arr) { int n = arr.length; int[] left = new int[n]; int[] right = new int[n]; int ans = -(1 << 30); for (int i = 0, s = 0; i < n; ++i) { s = Math.max(s, 0) + arr[i]; left[i] = s; ans = Math.max(ans, left[i]); } for (int i = n - 1, s = 0; i >= 0; --i) { s = Math.max(s, 0) + arr[i]; right[i] = s; } for (int i = 1; i < n - 1; ++i) { ans = Math.max(ans, left[i - 1] + right[i + 1]); } return ans; }}class Solution {public: int maximumSum(vector<int> &arr) { int n = arr.size(); int left[n]; int right[n]; for (int i = 0, s = 0; i < n; ++i) { s = max(s, 0) + arr[i]; left[i] = s; } for (int i = n - 1, s = 0; ~i; --i) { s = max(s, 0) + arr[i]; right[i] = s; } int ans = *max_element(left, left + n); for (int i = 1; i < n - 1; ++i) { ans = max(ans, left[i - 1] + right[i + 1]); } return ans; }};class Solution: def maximumSum(self, arr: List[int]) -> int: n = len(arr) left = [0] * n right = [0] * n s = 0 for i, x in enumerate(arr): s = max(s, 0) + x left[i] = s s = 0 for i in range(n - 1, - 1, - 1): s = max(s, 0) + arr[i] right[i] = s ans = max(left) for i in range(1, n - 1): ans = max(ans, left[i - 1] + right[i + 1]) return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.