Builds on Kadane's Algorithm, which you haven’t finished yet.
Kadane's Algorithm with One Deletion (Maximum Subarray Sum with One Deletion)
drop: the best sum ending here with one number deleted. Deleting x takes the old keep.You get an integer array arr. Choose a subarray, a run of numbers that sit next to each other. You may delete at most one number from the subarray. After the deletion, the subarray must still hold at least one number.
Return the largest possible sum of the subarray after the optional deletion.
[1, -2, 0, 3] and delete -2. The remaining numbers 1, 0, 3 sum to 4.Kadane with One Deletion
keep = no deletion, drop = one deletion ➔ drop = max(keep, drop + x) before keep changes ➔ best of both| 1 | def maximum_sum(arr: list[int]) -> int: |
| 2 | """Maximum Subarray Sum with One Deletion: Kadane's Algorithm with a second state for one deleted number. |
| 3 | Time: O(N) | Space: O(1) |
| 4 | """ |
| 5 | # keep: the best sum of a subarray ending at the current number with nothing deleted; drop: the same with exactly one number deleted |
| 6 | keep = best = arr[0] |
| 7 | drop = float('-inf') |
| 8 | for x in arr[1:]: |
| 9 | # 1. Delete x, Or Keep Deleting Earlier: |
| 10 | # Deleting x leaves the best run that ended just before x, so drop uses the old keep. A run that already deleted a number just adds x. |
| 11 | drop = max(keep, drop + x) |
| 12 | |
| 13 | # 2. Extend Or Start Fresh: |
| 14 | # The same rule as Kadane's Algorithm, with nothing deleted. |
| 15 | keep = max(x, keep + x) |
| 16 | |
| 17 | # 3. Track The Best: |
| 18 | # The best subarray may have deleted a number or not. |
| 19 | best = max(best, keep, drop) |
| 20 | |
| 21 | return best |
x, keep is the best sum of a subarray ending at x with nothing deleted, drop is the best sum of a subarray with one number deleted that ends at x, or ends just before x when x is the deleted number, and best is the largest of both seen so far.With one allowed deletion, a run ending at x is in one of two states: nothing deleted (keep) or one number deleted (drop). Deleting x itself leaves the best run that ended just before x, and a run that already deleted a number just adds x, so drop = max(keep, drop + x). keep is the unchanged Kadane step, and best takes both states. The trap is the order: update drop before keep, because if keep changes first, drop deletes a number from a run that already holds x.
See it run
Kadane with One Deletion, step by step
keep 1drop -infbest 1
Each column is one index of arr. The rows keep and drop fill in as the steps run.
xthe number being readkeepthe best run sum that ends at x with nothing deletedold keepkeep from the previous index, used to update drop firstdropthe best run sum that ends at x with one number deleted
Start: keep = best = arr[0] = 1 and drop = -inf. No run with a deletion exists yet, because deleting the only number leaves an empty run.
Gotcha 1 of 3
- The trap
Gotcha 1: Updating
keepbeforedropMistakekeep = max(x, keep + x) drop = max(keep, drop + x)Fixdrop = max(keep, drop + x) keep = max(x, keep + x)What happens: Deleting
xmust leave the best run that ended beforex. Ifkeepis updated first,keepalready includesx, sodroptakeskeepwithxin it and nothing is deleted.Example: Maximum Subarray Sum with One Deletion (LC 1186): arr = [1, -3, 1]Mistake: returns 1Fix: returns 2
- No number can be deleted from the run, so one running value
curris enough, as in Maximum Subarray (LC 53). See Kadane's Algorithm
Deep Dive
Why a second state is needed
In Kadane's Algorithm, a run ending at x has one best sum. Here the run may also delete one number. A run that has used its deletion and a run that has not are different situations, and they continue differently.
So the card keeps two sums for the subarrays that end at the current number. keep is the best sum with nothing deleted. drop is the best sum with one number deleted. The run must still hold at least one number after the deletion.
keep is the unchanged Kadane step. Everything this card adds is drop.
Why drop = max(keep, drop + x) is correct
Look at a run that has deleted one number, when the loop reaches x. There are two cases.
In the first case, the deleted number is x itself. Then the run is the best run that ended just before x, with nothing deleted. Its sum is the old keep.
In the second case, the deleted number is earlier. Then the run before x already used its deletion, and x is added. The sum is the old drop plus x.
The two cases cover every run with one deletion, so drop is the larger one.
In the first case, keep must be the old value, before it includes x. This is why drop is updated before keep.
The start, the empty run and best
The loop starts with keep = best = arr[0] and drop = float('-inf'). At index 0, no run with a deletion exists, because deleting the only number leaves an empty run. Minus infinity means that this state cannot be used yet.
At the next number, drop = max(keep, -inf + x) is the old keep, which is arr[0]. It is the run [arr[0]] after deleting arr[1].
The deletion is optional, so best = max(best, keep, drop) takes both states. A single number or an array with no useful deletion is still handled by keep.
Trace for arr = [1, -2, 0, 3], as x, keep, drop, best:
1, 1, -inf, 1 (the start).-2, -1, 1, 1.0, 0, 1, 1.3, 3, 4, 4.
The answer is 4. It is the run [1, 0, 3] after deleting -2.
The cost and a related problem
There are N states. Each does two max calls and one comparison. The time is O(N) and the space is O(1).
I compared maximum_sum with a brute force that tries every subarray and every optional deleted number, with at least one number left. They agreed on 10,000 random arrays, including all-negative arrays, arrays of one number and arrays with zeros.
Longest Subarray of 1's After Deleting One Element (LC 1493) has the same two states, but they hold lengths. There the deletion is required, so the answer is the largest drop, and not the largest of both states.
60-Second Pitch
"This is Kadane's Algorithm with a second state. keep is the best sum ending here with nothing deleted. drop is the best sum ending here with one number deleted. Deleting the current number takes the old keep. Deleting an earlier number adds x to the old drop. So drop = max(keep, drop + x), and I compute it before I update keep. drop starts at minus infinity, so a deletion never leaves an empty subarray. best takes the larger of both states. The time is O(N) and the space is O(1)."
Common Questions
- Why must
dropbe updated beforekeep? When the deleted number is
x,droptakes the best run that ended beforex. That is the oldkeep. Ifkeepis updated first, it already includesx, sodropholds a run with nothing deleted. For[1, -3, 1], the wrong order returns 1 and the correct answer is 2.- Why does
dropstart at minus infinity? At index 0, a run with a deletion would be empty, which is not allowed. Minus infinity means that this state does not exist yet. At the next number,
dropbecomes the oldkeep, which is a real run.- What is the answer when every number is negative?
The largest single number, because the subarray must keep at least one number. For
[-1, -1, -1, -1], the answer is -1. A version that allows an empty run returns 0.- What is the time and space complexity?
O(N)time, because one loop updates both states.O(1)space, because onlykeep,dropandbestexist.- How does Longest Subarray of 1's After Deleting One Element (LC 1493) differ?
The two states hold lengths. A 1 sets
droptomax(keep, drop + 1)and then adds 1 tokeep. A 0 setsdroptokeepand resetskeepto 0. The deletion is required there, so the answer is the largestdrop.
SWE Reasoning
Spot it
- The question asks for the best sum of one unbroken run of an array.
- You may delete at most one number from the run.
- The deletion may be a number in the middle, which a plain subarray cannot skip.
- Plain Kadane's Algorithm fails on a small example such as
[1, -3, 1]. - One value per position is not enough, because a run with a used deletion continues differently from a run without one.
The invariant, step by step
- Before the loop,
keepandbestarearr[0], anddropis minus infinity. No run with a deletion exists yet. - For each
x,drop = max(keep, drop + x)runs first. It reads the oldkeep, which does not includex. - The first choice deletes
x. The second choice addsxto a run that already deleted a number. keep = max(x, keep + x)is the Kadane step with nothing deleted.best = max(best, keep, drop)keeps the largest sum of either state.- After the last number,
bestis the answer.
Traps
- Wrong order. If
keepchanges first,dropcopies a run that already holdsx, and nothing is deleted. For[1, -3, 1], the result is 1 instead of 2. - Empty run after the deletion. Adding 0 to the
maxofdropallows an empty subarray. For[-1, -1, -1, -1], the result is 0 instead of -1. - Returning only
keep. This is plain Kadane's Algorithm and never uses the deletion. - Starting
dropat 0. At index 0, deleting the only number would leave an empty run. Start with minus infinity. - Treating the deletion as required. In this problem, the deletion is optional, so
besttakeskeeptoo.
Practice
Builds on Kadane's Algorithm, which you haven’t finished yet.
Kadane's Algorithm with One Deletion (Maximum Subarray Sum with One Deletion)
drop: the best sum ending here with one number deleted. Deleting x takes the old keep.You get an integer array arr. Choose a subarray, a run of numbers that sit next to each other. You may delete at most one number from the subarray. After the deletion, the subarray must still hold at least one number.
Return the largest possible sum of the subarray after the optional deletion.
[1, -2, 0, 3] and delete -2. The remaining numbers 1, 0, 3 sum to 4.Kadane with One Deletion
keep = no deletion, drop = one deletion ➔ drop = max(keep, drop + x) before keep changes ➔ best of both| 1 | def maximum_sum(arr: list[int]) -> int: |
| 2 | """Maximum Subarray Sum with One Deletion: Kadane's Algorithm with a second state for one deleted number. |
| 3 | Time: O(N) | Space: O(1) |
| 4 | """ |
| 5 | # keep: the best sum of a subarray ending at the current number with nothing deleted; drop: the same with exactly one number deleted |
| 6 | keep = best = arr[0] |
| 7 | drop = float('-inf') |
| 8 | for x in arr[1:]: |
| 9 | # 1. Delete x, Or Keep Deleting Earlier: |
| 10 | # Deleting x leaves the best run that ended just before x, so drop uses the old keep. A run that already deleted a number just adds x. |
| 11 | drop = max(keep, drop + x) |
| 12 | |
| 13 | # 2. Extend Or Start Fresh: |
| 14 | # The same rule as Kadane's Algorithm, with nothing deleted. |
| 15 | keep = max(x, keep + x) |
| 16 | |
| 17 | # 3. Track The Best: |
| 18 | # The best subarray may have deleted a number or not. |
| 19 | best = max(best, keep, drop) |
| 20 | |
| 21 | return best |
x, keep is the best sum of a subarray ending at x with nothing deleted, drop is the best sum of a subarray with one number deleted that ends at x, or ends just before x when x is the deleted number, and best is the largest of both seen so far.With one allowed deletion, a run ending at x is in one of two states: nothing deleted (keep) or one number deleted (drop). Deleting x itself leaves the best run that ended just before x, and a run that already deleted a number just adds x, so drop = max(keep, drop + x). keep is the unchanged Kadane step, and best takes both states. The trap is the order: update drop before keep, because if keep changes first, drop deletes a number from a run that already holds x.
See it run
Kadane with One Deletion, step by step
keep 1drop -infbest 1
Each column is one index of arr. The rows keep and drop fill in as the steps run.
xthe number being readkeepthe best run sum that ends at x with nothing deletedold keepkeep from the previous index, used to update drop firstdropthe best run sum that ends at x with one number deleted
Start: keep = best = arr[0] = 1 and drop = -inf. No run with a deletion exists yet, because deleting the only number leaves an empty run.
Gotcha 1 of 3
- The trap
Gotcha 1: Updating
keepbeforedropMistakekeep = max(x, keep + x) drop = max(keep, drop + x)Fixdrop = max(keep, drop + x) keep = max(x, keep + x)What happens: Deleting
xmust leave the best run that ended beforex. Ifkeepis updated first,keepalready includesx, sodroptakeskeepwithxin it and nothing is deleted.Example: Maximum Subarray Sum with One Deletion (LC 1186): arr = [1, -3, 1]Mistake: returns 1Fix: returns 2
- No number can be deleted from the run, so one running value
curris enough, as in Maximum Subarray (LC 53). See Kadane's Algorithm
Deep Dive
Why a second state is needed
In Kadane's Algorithm, a run ending at x has one best sum. Here the run may also delete one number. A run that has used its deletion and a run that has not are different situations, and they continue differently.
So the card keeps two sums for the subarrays that end at the current number. keep is the best sum with nothing deleted. drop is the best sum with one number deleted. The run must still hold at least one number after the deletion.
keep is the unchanged Kadane step. Everything this card adds is drop.
Why drop = max(keep, drop + x) is correct
Look at a run that has deleted one number, when the loop reaches x. There are two cases.
In the first case, the deleted number is x itself. Then the run is the best run that ended just before x, with nothing deleted. Its sum is the old keep.
In the second case, the deleted number is earlier. Then the run before x already used its deletion, and x is added. The sum is the old drop plus x.
The two cases cover every run with one deletion, so drop is the larger one.
In the first case, keep must be the old value, before it includes x. This is why drop is updated before keep.
The start, the empty run and best
The loop starts with keep = best = arr[0] and drop = float('-inf'). At index 0, no run with a deletion exists, because deleting the only number leaves an empty run. Minus infinity means that this state cannot be used yet.
At the next number, drop = max(keep, -inf + x) is the old keep, which is arr[0]. It is the run [arr[0]] after deleting arr[1].
The deletion is optional, so best = max(best, keep, drop) takes both states. A single number or an array with no useful deletion is still handled by keep.
Trace for arr = [1, -2, 0, 3], as x, keep, drop, best:
1, 1, -inf, 1 (the start).-2, -1, 1, 1.0, 0, 1, 1.3, 3, 4, 4.
The answer is 4. It is the run [1, 0, 3] after deleting -2.
The cost and a related problem
There are N states. Each does two max calls and one comparison. The time is O(N) and the space is O(1).
I compared maximum_sum with a brute force that tries every subarray and every optional deleted number, with at least one number left. They agreed on 10,000 random arrays, including all-negative arrays, arrays of one number and arrays with zeros.
Longest Subarray of 1's After Deleting One Element (LC 1493) has the same two states, but they hold lengths. There the deletion is required, so the answer is the largest drop, and not the largest of both states.
60-Second Pitch
"This is Kadane's Algorithm with a second state. keep is the best sum ending here with nothing deleted. drop is the best sum ending here with one number deleted. Deleting the current number takes the old keep. Deleting an earlier number adds x to the old drop. So drop = max(keep, drop + x), and I compute it before I update keep. drop starts at minus infinity, so a deletion never leaves an empty subarray. best takes the larger of both states. The time is O(N) and the space is O(1)."
Common Questions
- Why must
dropbe updated beforekeep? When the deleted number is
x,droptakes the best run that ended beforex. That is the oldkeep. Ifkeepis updated first, it already includesx, sodropholds a run with nothing deleted. For[1, -3, 1], the wrong order returns 1 and the correct answer is 2.- Why does
dropstart at minus infinity? At index 0, a run with a deletion would be empty, which is not allowed. Minus infinity means that this state does not exist yet. At the next number,
dropbecomes the oldkeep, which is a real run.- What is the answer when every number is negative?
The largest single number, because the subarray must keep at least one number. For
[-1, -1, -1, -1], the answer is -1. A version that allows an empty run returns 0.- What is the time and space complexity?
O(N)time, because one loop updates both states.O(1)space, because onlykeep,dropandbestexist.- How does Longest Subarray of 1's After Deleting One Element (LC 1493) differ?
The two states hold lengths. A 1 sets
droptomax(keep, drop + 1)and then adds 1 tokeep. A 0 setsdroptokeepand resetskeepto 0. The deletion is required there, so the answer is the largestdrop.
SWE Reasoning
Spot it
- The question asks for the best sum of one unbroken run of an array.
- You may delete at most one number from the run.
- The deletion may be a number in the middle, which a plain subarray cannot skip.
- Plain Kadane's Algorithm fails on a small example such as
[1, -3, 1]. - One value per position is not enough, because a run with a used deletion continues differently from a run without one.
The invariant, step by step
- Before the loop,
keepandbestarearr[0], anddropis minus infinity. No run with a deletion exists yet. - For each
x,drop = max(keep, drop + x)runs first. It reads the oldkeep, which does not includex. - The first choice deletes
x. The second choice addsxto a run that already deleted a number. keep = max(x, keep + x)is the Kadane step with nothing deleted.best = max(best, keep, drop)keeps the largest sum of either state.- After the last number,
bestis the answer.
Traps
- Wrong order. If
keepchanges first,dropcopies a run that already holdsx, and nothing is deleted. For[1, -3, 1], the result is 1 instead of 2. - Empty run after the deletion. Adding 0 to the
maxofdropallows an empty subarray. For[-1, -1, -1, -1], the result is 0 instead of -1. - Returning only
keep. This is plain Kadane's Algorithm and never uses the deletion. - Starting
dropat 0. At index 0, deleting the only number would leave an empty run. Start with minus infinity. - Treating the deletion as required. In this problem, the deletion is optional, so
besttakeskeeptoo.