1186. Maximum Subarray Sum with One Deletion
Problem Description
You are given an array of integers and need to find the maximum sum of a non-empty subarray (contiguous elements) where you can optionally delete at most one element from the subarray.
The key requirements are:
- The subarray must contain contiguous elements from the original array
- You can choose to delete either 0 or 1 element from your chosen subarray
- After any deletion, the subarray must still be non-empty (contain at least one element)
- You want to maximize the sum of the remaining elements
For example, if you have the array [1, -2, 0, 3]
:
- You could choose the subarray
[1, -2, 0, 3]
and delete-2
to get sum =1 + 0 + 3 = 4
- You could choose the subarray
[0, 3]
without deleting anything to get sum =0 + 3 = 3
- You could choose just
[3]
without deleting anything to get sum =3
The goal is to find the maximum possible sum among all valid choices of subarrays and deletion options.
Intuition
When thinking about this problem, we need to consider two scenarios: either we don't delete any element, or we delete exactly one element from our subarray.
If we don't delete any element, this becomes the classic maximum subarray sum problem (Kadane's algorithm), where we find the contiguous subarray with the largest sum.
The interesting part is when we delete one element. If we delete element at position i
, we're essentially connecting two separate subarrays: one that ends right before position i
, and another that starts right after position i
. The sum would be the maximum subarray ending at position i-1
plus the maximum subarray starting at position i+1
.
This observation leads us to think about preprocessing: What if we could know, for each position, the maximum subarray sum ending at that position? And similarly, the maximum subarray sum starting from that position?
We can build two arrays:
left[i]
: stores the maximum subarray sum ending at positioni
right[i]
: stores the maximum subarray sum starting at positioni
For left[i]
, we use the logic: either extend the previous maximum subarray by including arr[i]
, or start fresh from arr[i]
if the previous sum was negative. This gives us left[i] = max(left[i-1], 0) + arr[i]
.
Similarly, right[i]
can be computed by traversing from right to left.
Once we have these arrays, finding the answer becomes straightforward:
- Maximum without deletion:
max(left[i])
for alli
- Maximum with one deletion at position
i
:left[i-1] + right[i+1]
We take the maximum of all these possibilities to get our final answer.
Learn more about Dynamic Programming patterns.
Solution Approach
The solution uses preprocessing combined with enumeration to find the maximum sum.
Step 1: Build the left
array
We traverse the array from left to right, computing the maximum subarray sum ending at each position using Kadane's algorithm:
s = 0
for i, x in enumerate(arr):
s = max(s, 0) + x
left[i] = s
For each position i
, we decide whether to extend the previous subarray (s
) or start fresh. If s
is negative, we reset it to 0 before adding the current element x
. This ensures left[i]
contains the maximum sum of any subarray ending at position i
.
Step 2: Build the right
array
Similarly, we traverse the array from right to left to compute the maximum subarray sum starting at each position:
s = 0
for i in range(n - 1, -1, -1):
s = max(s, 0) + arr[i]
right[i] = s
This gives us right[i]
as the maximum sum of any subarray starting at position i
.
Step 3: Find the maximum sum without deletion
The maximum subarray sum without any deletion is simply the maximum value in the left
array:
ans = max(left)
Step 4: Consider deleting one element
For each possible deletion position i
(from index 1 to n-2, since we need at least one element on each side), we calculate the sum of connecting the maximum subarray ending at i-1
with the maximum subarray starting at i+1
:
for i in range(1, n - 1):
ans = max(ans, left[i - 1] + right[i + 1])
We skip positions 0 and n-1 because deleting them wouldn't connect two subarrays—it would just truncate one end.
Step 5: Return the result
The final answer is the maximum value found across all scenarios (with or without deletion).
The time complexity is O(n)
for building both arrays and O(n)
for finding the maximum, giving us O(n)
overall. The space complexity is O(n)
for the two auxiliary arrays.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with the array [2, -1, 3, -2, 4]
.
Step 1: Build the left
array (max subarray sum ending at each position)
Starting with s = 0
:
- Position 0:
s = max(0, 0) + 2 = 2
, soleft[0] = 2
- Position 1:
s = max(2, 0) + (-1) = 1
, soleft[1] = 1
- Position 2:
s = max(1, 0) + 3 = 4
, soleft[2] = 4
- Position 3:
s = max(4, 0) + (-2) = 2
, soleft[3] = 2
- Position 4:
s = max(2, 0) + 4 = 6
, soleft[4] = 6
Result: left = [2, 1, 4, 2, 6]
Step 2: Build the right
array (max subarray sum starting at each position)
Starting from the right with s = 0
:
- Position 4:
s = max(0, 0) + 4 = 4
, soright[4] = 4
- Position 3:
s = max(4, 0) + (-2) = 2
, soright[3] = 2
- Position 2:
s = max(2, 0) + 3 = 5
, soright[2] = 5
- Position 1:
s = max(5, 0) + (-1) = 4
, soright[1] = 4
- Position 0:
s = max(4, 0) + 2 = 6
, soright[0] = 6
Result: right = [6, 4, 5, 2, 4]
Step 3: Find maximum without deletion
Maximum of left
array = max([2, 1, 4, 2, 6]) = 6
This corresponds to the subarray [2, -1, 3, -2, 4]
with sum 6.
Step 4: Consider deleting one element
For each possible deletion position (indices 1 to 3):
- Delete index 1 (element -1):
left[0] + right[2] = 2 + 5 = 7
- This connects subarray
[2]
with subarray[3, -2, 4]
- This connects subarray
- Delete index 2 (element 3):
left[1] + right[3] = 1 + 2 = 3
- This connects subarray
[2, -1]
with subarray[-2, 4]
- This connects subarray
- Delete index 3 (element -2):
left[2] + right[4] = 4 + 4 = 8
- This connects subarray
[2, -1, 3]
with subarray[4]
- This connects subarray
Step 5: Return the result
Maximum value found = max(6, 7, 3, 8) = 8
The optimal solution is to choose the entire array [2, -1, 3, -2, 4]
and delete the element at index 3 (value -2), giving us [2, -1, 3, 4]
with sum = 8.
Solution Implementation
1class Solution:
2 def maximumSum(self, arr: List[int]) -> int:
3 n = len(arr)
4
5 # left[i] stores the maximum subarray sum ending at index i
6 max_sum_ending_here_left = [0] * n
7
8 # right[i] stores the maximum subarray sum starting at index i
9 max_sum_starting_here_right = [0] * n
10
11 # Build the left array using Kadane's algorithm from left to right
12 current_sum = 0
13 for i in range(n):
14 current_sum = max(current_sum, 0) + arr[i]
15 max_sum_ending_here_left[i] = current_sum
16
17 # Build the right array using Kadane's algorithm from right to left
18 current_sum = 0
19 for i in range(n - 1, -1, -1):
20 current_sum = max(current_sum, 0) + arr[i]
21 max_sum_starting_here_right[i] = current_sum
22
23 # Initialize answer with maximum subarray sum without deletion
24 max_result = max(max_sum_ending_here_left)
25
26 # Try deleting each element (except first and last) and calculate the sum
27 # by connecting the maximum subarray ending before it with the maximum
28 # subarray starting after it
29 for i in range(1, n - 1):
30 max_result = max(max_result,
31 max_sum_ending_here_left[i - 1] + max_sum_starting_here_right[i + 1])
32
33 return max_result
34
1class Solution {
2 public int maximumSum(int[] arr) {
3 int n = arr.length;
4
5 // maxSumEndingAt[i] stores the maximum subarray sum ending at index i (Kadane's algorithm)
6 int[] maxSumEndingAt = new int[n];
7
8 // maxSumStartingAt[i] stores the maximum subarray sum starting at index i
9 int[] maxSumStartingAt = new int[n];
10
11 // Initialize with a very small value (negative infinity)
12 int maxResult = Integer.MIN_VALUE;
13
14 // Build maxSumEndingAt array using Kadane's algorithm (left to right)
15 // This calculates the maximum subarray sum ending at each position
16 for (int i = 0, currentSum = 0; i < n; i++) {
17 // Either extend the existing subarray or start a new one from current element
18 currentSum = Math.max(currentSum, 0) + arr[i];
19 maxSumEndingAt[i] = currentSum;
20
21 // Update the maximum result (case where no element is deleted)
22 maxResult = Math.max(maxResult, maxSumEndingAt[i]);
23 }
24
25 // Build maxSumStartingAt array using Kadane's algorithm (right to left)
26 // This calculates the maximum subarray sum starting at each position
27 for (int i = n - 1, currentSum = 0; i >= 0; i--) {
28 // Either extend the existing subarray or start a new one from current element
29 currentSum = Math.max(currentSum, 0) + arr[i];
30 maxSumStartingAt[i] = currentSum;
31 }
32
33 // Check the case where we delete one element (element at index i)
34 // The maximum sum would be: max sum ending before i + max sum starting after i
35 for (int i = 1; i < n - 1; i++) {
36 // Connect the subarray ending at (i-1) with the subarray starting at (i+1)
37 // This effectively deletes element at index i
38 maxResult = Math.max(maxResult, maxSumEndingAt[i - 1] + maxSumStartingAt[i + 1]);
39 }
40
41 return maxResult;
42 }
43}
44
1class Solution {
2public:
3 int maximumSum(vector<int>& arr) {
4 int n = arr.size();
5
6 // maxSumEndingAt[i] stores the maximum subarray sum ending at index i
7 vector<int> maxSumEndingAt(n);
8
9 // maxSumStartingAt[i] stores the maximum subarray sum starting at index i
10 vector<int> maxSumStartingAt(n);
11
12 // Build the maximum subarray sum ending at each position
13 // Using Kadane's algorithm from left to right
14 for (int i = 0, currentSum = 0; i < n; ++i) {
15 currentSum = max(currentSum, 0) + arr[i];
16 maxSumEndingAt[i] = currentSum;
17 }
18
19 // Build the maximum subarray sum starting at each position
20 // Using Kadane's algorithm from right to left
21 for (int i = n - 1, currentSum = 0; i >= 0; --i) {
22 currentSum = max(currentSum, 0) + arr[i];
23 maxSumStartingAt[i] = currentSum;
24 }
25
26 // Case 1: Maximum sum without deleting any element
27 // This is the maximum value in maxSumEndingAt array
28 int maxSum = *max_element(maxSumEndingAt.begin(), maxSumEndingAt.end());
29
30 // Case 2: Maximum sum with deleting exactly one element
31 // For each possible deletion position i (excluding first and last),
32 // connect the maximum subarray ending at i-1 with the one starting at i+1
33 for (int i = 1; i < n - 1; ++i) {
34 maxSum = max(maxSum, maxSumEndingAt[i - 1] + maxSumStartingAt[i + 1]);
35 }
36
37 return maxSum;
38 }
39};
40
1/**
2 * Finds the maximum sum of subarray(s) in the given array.
3 * This algorithm handles the case where we can optionally delete one element
4 * to maximize the sum.
5 *
6 * @param arr - The input array of numbers
7 * @returns The maximum sum achievable
8 */
9function maximumSum(arr: number[]): number {
10 const arrayLength: number = arr.length;
11
12 // Array to store maximum subarray sum ending at index i (from left)
13 const maxSumEndingHere: number[] = Array(arrayLength).fill(0);
14
15 // Array to store maximum subarray sum starting at index i (from right)
16 const maxSumStartingHere: number[] = Array(arrayLength).fill(0);
17
18 // Calculate maximum subarray sum ending at each position (Kadane's algorithm from left)
19 for (let index = 0, currentSum = 0; index < arrayLength; ++index) {
20 // Either extend the existing subarray or start a new one
21 currentSum = Math.max(currentSum, 0) + arr[index];
22 maxSumEndingHere[index] = currentSum;
23 }
24
25 // Calculate maximum subarray sum starting at each position (Kadane's algorithm from right)
26 for (let index = arrayLength - 1, currentSum = 0; index >= 0; --index) {
27 // Either extend the existing subarray or start a new one
28 currentSum = Math.max(currentSum, 0) + arr[index];
29 maxSumStartingHere[index] = currentSum;
30 }
31
32 // Find the maximum sum without deleting any element
33 let maximumResult: number = Math.max(...maxSumEndingHere);
34
35 // Check if deleting one element gives a better result
36 // We skip the deleted element and connect the left and right subarrays
37 for (let deleteIndex = 1; deleteIndex < arrayLength - 1; ++deleteIndex) {
38 const sumWithDeletion: number = maxSumEndingHere[deleteIndex - 1] + maxSumStartingHere[deleteIndex + 1];
39 maximumResult = Math.max(maximumResult, sumWithDeletion);
40 }
41
42 return maximumResult;
43}
44
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array arr
. This is because:
- The first loop iterates through all
n
elements to build theleft
array:O(n)
- The second loop iterates through all
n
elements in reverse to build theright
array:O(n)
- Finding the maximum of the
left
array takesO(n)
- The final loop iterates through
n-2
elements to find the maximum sum:O(n)
- Total:
O(n) + O(n) + O(n) + O(n) = O(n)
The space complexity is O(n)
, where n
is the length of the array arr
. This is because:
- The
left
array storesn
elements:O(n)
- The
right
array storesn
elements:O(n)
- Other variables (
s
,ans
,i
,x
) use constant space:O(1)
- Total:
O(n) + O(n) + O(1) = O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Incorrectly handling arrays with all negative numbers
The Issue:
When the array contains only negative numbers, the standard Kadane's algorithm implementation shown in the code will produce incorrect results. The line current_sum = max(current_sum, 0) + arr[i]
will reset to 0 whenever the sum becomes negative, which means for an all-negative array, each left[i]
and right[i]
will just equal arr[i]
itself.
However, the real problem occurs when trying to delete an element. If we have [-1, -2, -3]
and delete -3
, the code calculates left[1] + right[3]
(which is out of bounds) or similar invalid connections. More critically, when all numbers are negative, deleting the most negative number should give us the best sum, but the current approach might not handle this correctly.
Example of the problem:
arr = [-2, -1, -3] # After building arrays: # left = [-2, -1, -3] # right = [-1, -1, -3] # max(left) = -1 (correct for no deletion) # But when considering deletion at index 1: # left[0] + right[2] = -2 + (-3) = -5 # This gives us -5 when we should get -2 (deleting -3 from [-2, -3])
The Fix: We need to ensure that when connecting subarrays after deletion, both sides are actually valid maximum subarrays, not just individual elements. Additionally, we need special handling to ensure we're considering the case where deleting an element from a subarray of length 2 gives us just one element:
class Solution:
def maximumSum(self, arr: List[int]) -> int:
n = len(arr)
if n == 1:
return arr[0]
# dp[i][0] = max subarray sum ending at i without deletion
# dp[i][1] = max subarray sum ending at i with at most one deletion
dp = [[float('-inf')] * 2 for _ in range(n)]
dp[0][0] = arr[0]
dp[0][1] = 0 # Can't delete from a single element and have non-empty result
result = arr[0]
for i in range(1, n):
# Without deletion
dp[i][0] = max(arr[i], dp[i-1][0] + arr[i])
# With deletion - either delete current element or keep it
dp[i][1] = max(
dp[i-1][0], # Delete current element
dp[i-1][1] + arr[i] # Already deleted something before
)
result = max(result, dp[i][0], dp[i][1])
return result
Pitfall 2: Edge case with single element array
The Issue:
The original code doesn't explicitly handle the case where the array has only one element. When n = 1
, the loop for i in range(1, n - 1)
doesn't execute at all (since range(1, 0)
is empty), which is actually fine. However, the code becomes harder to understand and verify for correctness.
The Fix: Add an explicit check at the beginning:
if n == 1: return arr[0]
Pitfall 3: Misunderstanding what constitutes a valid deletion
The Issue: The code only considers deleting elements at positions 1 through n-2, assuming we're always connecting two non-empty subarrays. However, we might want to delete the first or last element of our chosen subarray, which would mean we're not "connecting" anything but rather just taking a shorter subarray.
Example:
For array [5, -10, 2]
, the optimal solution might be to take subarray [5, -10]
and delete -10
, giving us just [5]
. The current approach wouldn't consider this case properly.
The Fix: Use dynamic programming that tracks both scenarios (with and without deletion) more explicitly, as shown in the solution for Pitfall 1. This approach naturally handles all deletion cases including edge deletions.
The three-steps of Depth First Search are:
- Identify states;
- Draw the state-space tree;
- DFS on the state-space tree.
Recommended Readings
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!