Longest run of ones after deleting one element
Given a binary array bits, you must delete exactly one element. Return the length of the
longest run of consecutive ones in what remains.
Examples
| Input | Output | Why |
|---|---|---|
bits = [1,1,0,1] | 3 | Delete the 0. |
bits = [0,1,1,1,0,1,1,0,1] | 5 | Delete the 0 at index 4: 1,1,1,1,1. |
bits = [1,1,1] | 2 | Something must be deleted, so one of the ones goes. |
Constraints
- 1 ≤ n ≤ 10⁵
- bits[i] is 0 or 1
Hints
Hint 1
Deleting a zero joins the runs of ones on either side of it. What does the stretch from the start of one to the end of the other look like?
Hint 2
It is a window with at most one zero. After the deletion its length is one less.
Hint 3
Take the longest window with at most one zero and subtract one. That also handles an array of all ones.
Where people lose marks
- Returning n for an array of all ones. A deletion is compulsory.
- Tracking runs on either side of each zero with separate arrays: correct, but more code and more edge cases than the window.
Dry run on [0,1,1,1,0,1,1,0,1]
Read it one frame at a time, the way you would trace the code on paper in an interview. Each frame shows the state after the step it describes.
-
Step 1 / 9bits00lo/hi1112130415160718zeros1best0
0 enters. The window 0..0 has 1 zero; after one deletion it gives 0 ones.
-
Step 2 / 9bits00lo11hi12130415160718zeros1best1
1 enters. The window 0..1 has 1 zero; after one deletion it gives 1 ones, the best so far.
-
Step 3 / 9bits00lo1112hi130415160718zeros1best2
1 enters. The window 0..2 has 1 zero; after one deletion it gives 2 ones, the best so far.
-
Step 4 / 9bits00lo111213hi0415160718zeros1best3
1 enters. The window 0..3 has 1 zero; after one deletion it gives 3 ones, the best so far.
-
Step 5 / 9bits0011lo121304hi15160718zeros1best3
0 enters. A second zero: move lo past the first, to 1. The window 1..4 has 1 zero; after one deletion it gives 3 ones.
-
Step 6 / 9bits0011lo12130415hi160718zeros1best4
1 enters. The window 1..5 has 1 zero; after one deletion it gives 4 ones, the best so far.
-
Step 7 / 9bits0011lo1213041516hi0718zeros1best5
1 enters. The window 1..6 has 1 zero; after one deletion it gives 5 ones, the best so far.
-
Step 8 / 9bits001112130415lo1607hi18zeros1best5
0 enters. A second zero: move lo past the first, to 5. The window 5..7 has 1 zero; after one deletion it gives 2 ones.
-
Step 9 / 9bits001112130415lo160718hizeros1best5
1 enters. The window 5..8 has 1 zero; after one deletion it gives 3 ones.
How to think about it
After the deletion, the best run of ones came from a stretch of the original array with at most one zero in it: either the zero was deleted, or the stretch was all ones and one of its ones was deleted (or a zero outside it was). Either way the run is one shorter than the stretch. So find the longest window with at most one zero, which is the flips problem with k = 1, and subtract one.
The all-ones case is where many solutions go wrong: there is no zero to delete, so the answer is n − 1, and the "subtract one" rule gives exactly that.
Complexity. Time O(n) · Space O(1)
The solution, in six languages
Each one is compiled and run against the test table before it is published. Java is reviewed by hand.
def longest_after_delete(bits):
best = lo = zeros = 0
for hi, b in enumerate(bits):
zeros += b == 0
while zeros > 1: # at most one zero, the one we delete
zeros -= bits[lo] == 0
lo += 1
best = max(best, hi - lo) # window length minus the deleted element
return best#include <vector>
#include <algorithm>
int solve(const std::vector<int>& bits) {
int best = 0, lo = 0, zeros = 0;
for (int hi = 0; hi < (int)bits.size(); ++hi) {
zeros += bits[hi] == 0;
while (zeros > 1) zeros -= bits[lo++] == 0; // at most one zero, the one we delete
best = std::max(best, hi - lo); // length minus the deleted element
}
return best;
}class Solution {
public int longestAfterDelete(int[] bits) {
int best = 0, lo = 0, zeros = 0;
for (int hi = 0; hi < bits.length; hi++) {
if (bits[hi] == 0) zeros++;
while (zeros > 1) if (bits[lo++] == 0) zeros--; // at most one zero
best = Math.max(best, hi - lo); // length minus the deleted element
}
return best;
}
}function longestAfterDelete(bits) {
let best = 0, lo = 0, zeros = 0;
for (let hi = 0; hi < bits.length; hi++) {
if (bits[hi] === 0) zeros++;
while (zeros > 1) if (bits[lo++] === 0) zeros--; // at most one zero, the one we delete
best = Math.max(best, hi - lo); // length minus the deleted element
}
return best;
}fn solve(bits: &[i64]) -> usize {
let (mut best, mut lo, mut zeros) = (0usize, 0usize, 0usize);
for hi in 0..bits.len() {
if bits[hi] == 0 { zeros += 1; }
while zeros > 1 { // at most one zero, the one we delete
if bits[lo] == 0 { zeros -= 1; }
lo += 1;
}
best = best.max(hi - lo); // length minus the deleted element
}
best
}import Data.Array
-- The longest window with at most one zero, minus the element that must be deleted.
solve :: [Int] -> Int
solve bits = third (foldl step (0, 0, 0) (zip [0 ..] bits))
where
arr = listArray (0, length bits - 1) bits
third (_, _, b) = b
step (lo, zeros, best) (hi, b) =
let (lo', z') = shrink lo (zeros + fromEnum (b == 0))
in (lo', z', max best (hi - lo'))
shrink lo z
| z > 1 = shrink (lo + 1) (z - fromEnum (arr ! lo == 0))
| otherwise = (lo, z)
Variants to try
- Delete up to k elements: the window allows k zeros and the answer subtracts the zeros it actually contains.
- Delete at most one, rather than exactly one: subtract one only if the window contains a zero.
Next problems
Longest run of tasks within a time budget
With positive numbers, a run that is over budget only gets worse as it grows, so the left end never moves back.
Open the problem → Medium15 minLongest run of distinct characters
The window that grows on the right and shrinks on the left, and the invariant that makes it correct.
Open the problem → Medium20 minLongest run with at most k distinct letters
The variable window in its plainest form: grow on the right, and while the window breaks the rule, shrink on the left.
Open the problem →Write a solution and run it against the real test table.
Keep going
Longest run of tasks within a time budget
With positive numbers, a run that is over budget only gets worse as it grows, so the left end never moves back.
Open the problem → Medium15 minLongest run of distinct characters
The window that grows on the right and shrinks on the left, and the invariant that makes it correct.
Open the problem → Medium20 minLongest run with at most k distinct letters
The variable window in its plainest form: grow on the right, and while the window breaks the rule, shrink on the left.
Open the problem → Sliding windowThe pattern behind it
A window that grows on the right, shrinks on the left, and holds an invariant at all times.
Read the pattern →Preparing for a real process? Quant interview preparation, or book a free 20-minute call.