Longest Subarray of 1s After Deleting One Element [Medium] — Sliding Window
Advertisement
Problem Statement
Given a binary array nums, you must delete exactly one element from the array. Return the length of the longest non-empty subarray consisting entirely of 1s in the resulting array.
Examples:
Input: nums = [1, 1, 0, 1]
Output: 3
Explanation: Delete the 0 at index 2. The result is [1,1,1], length 3.
Input: nums = [0, 1, 1, 1, 0, 1, 1, 0, 1]
Output: 5
Explanation: Delete the 0 at index 4. The result is [0,1,1,1,1,1,0,1], and the
longest subarray of 1s is [1,1,1,1,1], length 5.
Input: nums = [1, 1, 1]
Output: 2
Explanation: You must delete one element. Delete any 1. Result is [1,1], length 2.Constraints:
1 <= nums.length <= 100,000nums[i]is either0or1
Why This Problem Matters
This problem is essentially LC 1004 — Max Consecutive Ones III with k = 1, but with one critical twist: you must delete exactly one element, not at most one. That "must" changes the edge case for an all-ones array — instead of returning the full array length, you return length - 1 because you are forced to delete something.
Why do interviewers love this problem?
- It tests whether you really understand sliding window, not just memorised it. Candidates who blindly copy the "at most k zeros" template miss the off-by-one in the return statement.
- It probes edge-case awareness. What happens when the input has no zeros at all? What about a single element? These edge cases are free interview points if you mention them upfront.
- It bridges to harder problems. Understanding why the answer is
window_size - 1(notwindow_size) is the mental model that unlocks LC 487, LC 1004, and entire families of "flip at most k bits" problems.
Companies that have asked this or a direct variant in interviews include Amazon, Google, Meta, and Microsoft. It often appears as a warm-up before a harder two-pointer or binary search question.
The Sliding Window Insight
Let us build the intuition from first principles.
Step 1 — Reformulate the problem.
Deleting one element from a binary array is the same as "skipping over" one position. If we want the longest subarray of all 1s after deleting one element, we are really asking:
What is the longest contiguous window in the original array that contains at most one 0? Subtract 1 (for the deleted element slot) and that is your answer.
Step 2 — Why subtract 1?
When our window [left, right] contains exactly one 0, that 0 is the element we "delete." The resulting subarray of 1s has length right - left + 1 - 1 = right - left. When the window contains zero 0s (all 1s), we still must delete one 1, giving length right - left + 1 - 1 = right - left.
In both cases the answer is right - left — the window size minus 1. This is the single formula that handles every case, including the all-ones edge case.
Step 3 — Variable sliding window mechanics.
We maintain two pointers left and right and a counter zeros tracking how many 0s are inside the current window:
- Expand
rightone step at a time. Ifnums[right] == 0, incrementzeros. - If
zeros > 1, shrink from the left: ifnums[left] == 0, decrementzeros, then advanceleft. - At every step, candidate answer =
right - left.
The window never needs to shrink below size 1 because the problem guarantees the input is non-empty and we always delete exactly one element.
Visual Dry Run
Let us trace through nums = [0, 1, 1, 1, 0, 1, 1, 0, 1] step by step.
We track: left, right, zeros, and candidate = right - left.
Index: 0 1 2 3 4 5 6 7 8
nums: 0 1 1 1 0 1 1 0 1| Step | right | nums[right] | zeros | left | Window indices | candidate |
|---|---|---|---|---|---|---|
| 1 | 0 | 0 | 1 | 0 | [0..0] | 0 |
| 2 | 1 | 1 | 1 | 0 | [0..1] | 1 |
| 3 | 2 | 1 | 1 | 0 | [0..2] | 2 |
| 4 | 3 | 1 | 1 | 0 | [0..3] | 3 |
| 5 | 4 | 0 | 2 | 0 | too many zeros | shrink... |
| nums[0]=0, zeros=1, left=1 | ||||||
| 5b | 4 | — | 1 | 1 | [1..4] | 3 |
| 6 | 5 | 1 | 1 | 1 | [1..5] | 4 |
| 7 | 6 | 1 | 1 | 1 | [1..6] | 5 ← max |
| 8 | 7 | 0 | 2 | 1 | too many zeros | shrink... |
| nums[1]=1, zeros=2, left=2 | ||||||
| nums[2]=1, zeros=2, left=3 | ||||||
| nums[3]=1, zeros=2, left=4 | ||||||
| nums[4]=0, zeros=1, left=5 | ||||||
| 8b | 7 | — | 1 | 5 | [5..7] | 2 |
| 9 | 8 | 1 | 1 | 5 | [5..8] | 3 |
Final answer: 5 (window [1..6] = [1,1,1,0,1,1], delete the 0 at index 4, giving five 1s).
Key observations from the trace:
- The window
[1..6]spans indices 1 through 6 (six elements). Subtract the one 0 inside it = 5. - The formula
right - left=6 - 1= 5 captures this without us explicitly counting 1s. - When we had to shrink (step 8), we moved
leftall the way from 1 to 5 because we needed to evict the 0 at index 4.
Common Mistakes
Mistake 1 — Returning right - left + 1 instead of right - left.
This is the most common bug. If your window is [left..right] with one 0, the window has right - left + 1 elements total. After deleting the 0, you have right - left ones. The + 1 is consumed by the mandatory deletion. Candidates who write right - left + 1 will fail on the all-ones case: [1,1,1] would return 3, but the correct answer is 2.
Mistake 2 — Not handling the all-ones input.
Input [1, 1, 1] has no zeros. Some candidates think "no zero to delete, return 3." But the problem says you must delete exactly one element. The answer is 2. The right - left formula handles this automatically — the max window spans the whole array with zeros = 0, so right - left = 2 - 0 = 2. If you add a special case to handle "no zeros found, return n," you introduce a bug.
Mistake 3 — Shrinking with a while loop but decrementing zeros only on left advance.
A subtle off-by-one appears when candidates write:
# Buggy version
while zeros > 1:
zeros -= 1 # Wrong: should only decrement if nums[left] == 0
left += 1This decrements zeros even when nums[left] is 1, corrupting the count. Always guard: if nums[left] == 0: zeros -= 1 before left += 1.
Mistake 4 — Initialising ans as 0 and forgetting to update inside the loop.
If you compute ans only after the loop ends, you capture the final window, not the maximum window seen. Always update ans = max(ans, right - left) inside the loop at every step.
Mistake 5 — Confusing this with "at most k zeros, return window size."
LC 1004 returns right - left + 1 because you do not have to delete anything — you flip zeros to ones. LC 1493 returns right - left because you must delete. Mixing these up costs you the problem in an interview.
Solutions
Python
def longestSubarray(nums: list[int]) -> int:
left = 0 # left boundary of the sliding window
zeros = 0 # number of 0s inside the current window [left..right]
ans = 0 # best answer seen so far
for right in range(len(nums)):
# Expand window: include nums[right]
if nums[right] == 0:
zeros += 1 # one more zero entered the window
# Shrink window from the left until we have at most one 0
while zeros > 1:
if nums[left] == 0:
zeros -= 1 # a zero is leaving the window from the left
left += 1 # advance left boundary
# Window [left..right] has at most one 0.
# We must delete exactly one element (the 0, or any 1 if zeros==0).
# Either way the surviving subarray of 1s has length = right - left.
ans = max(ans, right - left)
return ansWhy right - left and not right - left + 1?
The window [left, right] contains right - left + 1 elements. One of them will be deleted (either the 0 if zeros == 1, or a forced 1 if zeros == 0). After deletion: right - left + 1 - 1 = right - left.
JavaScript
/**
* @param {number[]} nums
* @return {number}
*/
var longestSubarray = function(nums) {
let left = 0; // left pointer of the sliding window
let zeros = 0; // count of 0s inside the window [left..right]
let ans = 0; // maximum subarray length of 1s found so far
for (let right = 0; right < nums.length; right++) {
// Expand: bring nums[right] into the window
if (nums[right] === 0) {
zeros++; // a zero just entered from the right
}
// Shrink from the left until the window has at most one 0
while (zeros > 1) {
if (nums[left] === 0) {
zeros--; // a zero is leaving from the left
}
left++; // move left boundary inward
}
// Window [left..right] is valid (at most one 0).
// Subtract 1 for the mandatory deletion — answer is window size minus 1.
ans = Math.max(ans, right - left);
}
return ans;
};Complexity Analysis
| Metric | Value | Explanation |
|---|---|---|
| Time | O(n) | Each element enters and leaves the window at most once. Both right and left only move forward. |
| Space | O(1) | Only three integer variables: left, zeros, ans. No auxiliary data structures. |
The two-pointer sliding window is optimal here — you cannot solve this faster than O(n) because you must inspect every element at least once to know if it is 0 or 1.
Follow-up Questions
Interviewers frequently extend this problem. Here are the two most common follow-ups and how the insight transfers.
Follow-up 1 — What if you can delete at most k elements? (LC 1004)
LC 1004 — Max Consecutive Ones III gives you a budget of k flips. The window condition becomes zeros <= k instead of zeros <= 1. The return value changes to right - left + 1 because you do not have to delete anything — flipping a 0 to 1 keeps all elements in place.
# LC 1004 template (k flips allowed, no forced deletion)
def longestOnes(nums, k):
left = zeros = ans = 0
for right in range(len(nums)):
if nums[right] == 0:
zeros += 1
while zeros > k:
if nums[left] == 0:
zeros -= 1
left += 1
ans = max(ans, right - left + 1) # +1 here because nothing is deleted
return ansFollow-up 2 — At most two consecutive zeros? (LC 487)
LC 487 — Max Consecutive Ones II asks: flip at most one 0 to 1, return the longest subarray of 1s. This is k = 1 of LC 1004, but the return value is right - left + 1 (no forced deletion). It is the complement of LC 1493 — same window logic, different return formula.
The pattern across all three:
| Problem | Window condition | Return |
|---|---|---|
| LC 1493 | zeros <= 1, must delete | right - left |
| LC 487 | zeros <= 1, may flip | right - left + 1 |
| LC 1004 | zeros <= k, may flip | right - left + 1 |
Recognising this family in an interview signals strong pattern mastery.
This Pattern Solves
The "variable sliding window with a budget" pattern applies wherever you have:
- A contiguous subarray constraint (longest, shortest, count)
- A "budget" of allowed violations (zeros, mismatches, distinct characters)
- An expand-right / shrink-left loop that keeps violations within budget
Problems directly solved by this template:
- LC 3 — Longest Substring Without Repeating Characters (
budget = 0repeated chars) - LC 424 — Longest Repeating Character Replacement (
budget = knon-dominant chars) - LC 487 — Max Consecutive Ones II (
budget = 1zero flip) - LC 1004 — Max Consecutive Ones III (
budget = kzero flips) - LC 1493 — This problem (
budget = 1zero, forced deletion)
Once you internalise the template, the only things that change per problem are the "violation condition" and whether you return right - left or right - left + 1.
Key Takeaways
- The mandatory deletion means the window size minus 1 is the answer, not the window size — return
right - left(notright - left + 1). - Maintain a sliding window with at most 1 zero; shrink from the left when
zeros > 1. - This is LC 1004 (Max Consecutive Ones III with k=1) with the twist that even an all-1s array still deletes one element — handle this by noting the result is
n-1in that edge case (the window constraint handles it automatically viaright - left). - O(n) time, O(1) space — the most efficient possible solution for this type of problem.
- The pattern generalizes: "longest subarray with at most k violations" → sliding window with shrink on
violations > k, returnright - left + 1 - k(for mandatory deletions) orright - left + 1(without). - Amazon, Google, and Meta use this as a warm-up hard/medium to check comfort with the sliding window template before escalating to harder variants.
- Variation: if the problem said "at most one deletion" (optional), handle the all-1s case separately and return the full window length when
zeros == 0.
Advertisement