1493. Longest Subarray of 1's After Deleting One Element
Problem Description
You are given a binary array nums
(containing only 0s and 1s). You must delete exactly one element from this array.
After deleting one element, you need to find the longest subarray that contains only 1s. Return the length of this longest subarray. If no such subarray exists after deletion, return 0.
For example:
- If
nums = [1,1,0,1]
, after deleting the element at index 2 (the 0), we get[1,1,1]
, which gives us a subarray of length 3 containing only 1s. - If
nums = [0,1,1,1,0,1,1,0,1]
, we could delete the element at index 4 (the 0), which would connect the 1s on both sides, giving us[1,1,1,1,1]
as the longest subarray of 1s with length 5. - If
nums = [1,1,1]
, we must still delete one element. After deleting any 1, we're left with a subarray of length 2 containing only 1s.
The key insight is that deleting an element can potentially connect two groups of consecutive 1s if we delete a 0 between them, or it will simply reduce the length of a group of 1s if we delete a 1.
Intuition
The key observation is that when we delete an element at position i
, we're essentially trying to connect the consecutive 1s that appear before position i
with the consecutive 1s that appear after position i
.
Think about what happens when we delete different types of elements:
- If we delete a 0, we can potentially join two groups of 1s that were separated by this 0
- If we delete a 1, we break a sequence of 1s, but we still get the remaining 1s on either side
For any position i
that we choose to delete, the resulting length of consecutive 1s would be:
- The number of consecutive 1s ending right before position
i
- Plus the number of consecutive 1s starting right after position
i
This leads us to think about preprocessing the array. If we can know for each position:
- How many consecutive 1s end at or before this position (let's call this
left[i]
) - How many consecutive 1s start at or after this position (let's call this
right[i]
)
Then for each deletion position i
, we can quickly calculate the result as left[i] + right[i+1]
.
The preprocessing approach makes sense because:
left[i]
can be computed by scanning from left to right: ifnums[i]
is 1, thenleft[i] = left[i-1] + 1
, otherwise it's 0right[i]
can be computed by scanning from right to left: ifnums[i]
is 1, thenright[i] = right[i+1] + 1
, otherwise it's 0
By trying all possible deletion positions and taking the maximum, we get our answer. This transforms a potentially complex problem into a simple enumeration with preprocessing.
Learn more about Dynamic Programming and Sliding Window patterns.
Solution Approach
The solution uses a preprocessing approach with two auxiliary arrays to efficiently calculate the result for each possible deletion position.
Step 1: Initialize auxiliary arrays
We create two arrays left
and right
, both of size n+1
(where n
is the length of nums
). These arrays are initialized with zeros:
left[i]
will store the count of consecutive 1s ending at positioni-1
in the original arrayright[i]
will store the count of consecutive 1s starting at positioni
in the original array
Step 2: Build the left
array
We iterate through nums
from left to right (using 1-based indexing for left
):
for i, x in enumerate(nums, 1):
if x:
left[i] = left[i - 1] + 1
- If
nums[i-1]
is 1, thenleft[i] = left[i-1] + 1
(extending the consecutive 1s) - If
nums[i-1]
is 0, thenleft[i]
remains 0 (breaking the sequence)
Step 3: Build the right
array
We iterate through nums
from right to left:
for i in range(n - 1, -1, -1):
if nums[i]:
right[i] = right[i + 1] + 1
- If
nums[i]
is 1, thenright[i] = right[i+1] + 1
(extending the consecutive 1s from the right) - If
nums[i]
is 0, thenright[i]
remains 0
Step 4: Calculate the maximum
For each position i
that we could delete (from 0 to n-1
), we calculate:
left[i]
: consecutive 1s ending just before positioni
right[i+1]
: consecutive 1s starting just after positioni
- The sum
left[i] + right[i+1]
gives us the length of consecutive 1s after deleting element at positioni
We return the maximum value among all possible deletion positions:
return max(left[i] + right[i + 1] for i in range(n))
Time Complexity: O(n)
- We make three passes through the array
Space Complexity: O(n)
- We use two auxiliary arrays of size n+1
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 nums = [1,1,0,1,0,1,1]
.
Step 1: Initialize arrays
n = 7
left = [0,0,0,0,0,0,0,0]
(size 8)right = [0,0,0,0,0,0,0,0]
(size 8)
Step 2: Build the left
array
Processing from left to right:
- Index 0:
nums[0] = 1
, soleft[1] = left[0] + 1 = 0 + 1 = 1
- Index 1:
nums[1] = 1
, soleft[2] = left[1] + 1 = 1 + 1 = 2
- Index 2:
nums[2] = 0
, soleft[3] = 0
(sequence breaks) - Index 3:
nums[3] = 1
, soleft[4] = left[3] + 1 = 0 + 1 = 1
- Index 4:
nums[4] = 0
, soleft[5] = 0
(sequence breaks) - Index 5:
nums[5] = 1
, soleft[6] = left[5] + 1 = 0 + 1 = 1
- Index 6:
nums[6] = 1
, soleft[7] = left[6] + 1 = 1 + 1 = 2
Result: left = [0,1,2,0,1,0,1,2]
Step 3: Build the right
array
Processing from right to left:
- Index 6:
nums[6] = 1
, soright[6] = right[7] + 1 = 0 + 1 = 1
- Index 5:
nums[5] = 1
, soright[5] = right[6] + 1 = 1 + 1 = 2
- Index 4:
nums[4] = 0
, soright[4] = 0
(sequence breaks) - Index 3:
nums[3] = 1
, soright[3] = right[4] + 1 = 0 + 1 = 1
- Index 2:
nums[2] = 0
, soright[2] = 0
(sequence breaks) - Index 1:
nums[1] = 1
, soright[1] = right[2] + 1 = 0 + 1 = 1
- Index 0:
nums[0] = 1
, soright[0] = right[1] + 1 = 1 + 1 = 2
Result: right = [2,1,0,1,0,2,1,0]
Step 4: Calculate maximum for each deletion position
For each index i
, calculate left[i] + right[i+1]
:
- Delete index 0:
left[0] + right[1] = 0 + 1 = 1
- Delete index 1:
left[1] + right[2] = 1 + 0 = 1
- Delete index 2:
left[2] + right[3] = 2 + 1 = 3
✓ (connects two 1s with one 1) - Delete index 3:
left[3] + right[4] = 0 + 0 = 0
- Delete index 4:
left[4] + right[5] = 1 + 2 = 3
✓ (connects one 1 with two 1s) - Delete index 5:
left[5] + right[6] = 0 + 1 = 1
- Delete index 6:
left[6] + right[7] = 1 + 0 = 1
Maximum value: 3
This makes sense! When we delete either the 0 at index 2 or the 0 at index 4, we can connect groups of 1s to get a maximum length of 3.
Solution Implementation
1class Solution:
2 def longestSubarray(self, nums: List[int]) -> int:
3 """
4 Find the longest subarray of 1s after deleting exactly one element.
5
6 Args:
7 nums: List of binary integers (0s and 1s)
8
9 Returns:
10 Length of the longest subarray containing only 1s after deleting one element
11 """
12 n = len(nums)
13
14 # left[i] stores the count of consecutive 1s ending at position i-1
15 left = [0] * (n + 1)
16
17 # right[i] stores the count of consecutive 1s starting from position i
18 right = [0] * (n + 1)
19
20 # Build left array: count consecutive 1s from left to right
21 for i, num in enumerate(nums, 1):
22 if num == 1:
23 left[i] = left[i - 1] + 1
24 else:
25 left[i] = 0
26
27 # Build right array: count consecutive 1s from right to left
28 for i in range(n - 1, -1, -1):
29 if nums[i] == 1:
30 right[i] = right[i + 1] + 1
31 else:
32 right[i] = 0
33
34 # Find maximum length by trying to delete each element
35 # For each position i, combine consecutive 1s before i and after i
36 max_length = 0
37 for i in range(n):
38 max_length = max(max_length, left[i] + right[i + 1])
39
40 return max_length
41
1class Solution {
2 public int longestSubarray(int[] nums) {
3 int n = nums.length;
4
5 // left[i] stores the count of consecutive 1s ending at index i-1
6 int[] left = new int[n + 1];
7
8 // right[i] stores the count of consecutive 1s starting at index i
9 int[] right = new int[n + 1];
10
11 // Build the left array: count consecutive 1s from left to right
12 for (int i = 1; i <= n; ++i) {
13 if (nums[i - 1] == 1) {
14 left[i] = left[i - 1] + 1;
15 }
16 }
17
18 // Build the right array: count consecutive 1s from right to left
19 for (int i = n - 1; i >= 0; --i) {
20 if (nums[i] == 1) {
21 right[i] = right[i + 1] + 1;
22 }
23 }
24
25 // Find the maximum length by trying to remove each element
26 // and joining the consecutive 1s on both sides
27 int maxLength = 0;
28 for (int i = 0; i < n; ++i) {
29 // For each position, calculate the sum of consecutive 1s
30 // before position i and after position i
31 maxLength = Math.max(maxLength, left[i] + right[i + 1]);
32 }
33
34 return maxLength;
35 }
36}
37
1class Solution {
2public:
3 int longestSubarray(vector<int>& nums) {
4 int arraySize = nums.size();
5
6 // leftOnes[i] stores the count of consecutive 1s ending at position i-1
7 vector<int> leftOnes(arraySize + 1, 0);
8
9 // rightOnes[i] stores the count of consecutive 1s starting at position i
10 vector<int> rightOnes(arraySize + 1, 0);
11
12 // Build the leftOnes array
13 // For each position, if it's a 1, extend the consecutive count from the left
14 for (int i = 1; i <= arraySize; ++i) {
15 if (nums[i - 1] == 1) {
16 leftOnes[i] = leftOnes[i - 1] + 1;
17 }
18 }
19
20 // Build the rightOnes array
21 // For each position from right to left, if it's a 1, extend the consecutive count from the right
22 for (int i = arraySize - 1; i >= 0; --i) {
23 if (nums[i] == 1) {
24 rightOnes[i] = rightOnes[i + 1] + 1;
25 }
26 }
27
28 // Find the maximum length by trying to delete each element
29 // When we delete element at position i, we can connect:
30 // - consecutive 1s ending before position i (leftOnes[i])
31 // - consecutive 1s starting after position i (rightOnes[i + 1])
32 int maxLength = 0;
33 for (int i = 0; i < arraySize; ++i) {
34 int currentLength = leftOnes[i] + rightOnes[i + 1];
35 maxLength = max(maxLength, currentLength);
36 }
37
38 return maxLength;
39 }
40};
41
1/**
2 * Finds the longest subarray of 1s after deleting one element from the array
3 * @param nums - Binary array containing only 0s and 1s
4 * @returns The length of the longest subarray containing only 1s after deleting exactly one element
5 */
6function longestSubarray(nums: number[]): number {
7 const arrayLength: number = nums.length;
8
9 // Array to store count of consecutive 1s ending at each position (from left)
10 const consecutiveOnesFromLeft: number[] = Array(arrayLength + 1).fill(0);
11
12 // Array to store count of consecutive 1s starting at each position (from right)
13 const consecutiveOnesFromRight: number[] = Array(arrayLength + 1).fill(0);
14
15 // Calculate consecutive 1s from left to right
16 for (let i = 1; i <= arrayLength; ++i) {
17 if (nums[i - 1] === 1) {
18 // If current element is 1, extend the consecutive count
19 consecutiveOnesFromLeft[i] = consecutiveOnesFromLeft[i - 1] + 1;
20 }
21 }
22
23 // Calculate consecutive 1s from right to left
24 for (let i = arrayLength - 1; i >= 0; --i) {
25 if (nums[i] === 1) {
26 // If current element is 1, extend the consecutive count
27 consecutiveOnesFromRight[i] = consecutiveOnesFromRight[i + 1] + 1;
28 }
29 }
30
31 // Find maximum length by trying to delete each element
32 let maxLength: number = 0;
33 for (let i = 0; i < arrayLength; ++i) {
34 // For each position, combine consecutive 1s before and after deleting element at position i
35 const currentLength: number = consecutiveOnesFromLeft[i] + consecutiveOnesFromRight[i + 1];
36 maxLength = Math.max(maxLength, currentLength);
37 }
38
39 return maxLength;
40}
41
Time and Space Complexity
Time Complexity: O(n)
The algorithm consists of three main parts:
- First loop: Iterates through the array once to populate the
left
array, takingO(n)
time - Second loop: Iterates through the array once in reverse to populate the
right
array, takingO(n)
time - Final computation: Iterates through all indices to find the maximum of
left[i] + right[i + 1]
, takingO(n)
time
Since these operations are sequential and not nested, the overall time complexity is O(n) + O(n) + O(n) = O(n)
.
Space Complexity: O(n)
The algorithm uses additional space for:
left
array of sizen + 1
right
array of sizen + 1
Both arrays store intermediate results, requiring O(n + 1) + O(n + 1) = O(n)
extra space in total.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting the Mandatory Deletion Requirement
A common mistake is not handling the case where the array contains only 1s. In this scenario, we still must delete exactly one element, so the maximum length should be n-1
, not n
.
Incorrect approach:
# This would incorrectly return n for an array of all 1s
return max(left[i] + right[i + 1] for i in range(n))
Why it fails: For nums = [1,1,1]
, this would calculate:
- Position 0: left[0] + right[1] = 0 + 2 = 2
- Position 1: left[1] + right[2] = 1 + 1 = 2
- Position 2: left[2] + right[3] = 2 + 0 = 2
The result is correct (2), but only by coincidence due to how the arrays are built.
2. Off-by-One Errors in Array Indexing
The solution uses 1-based indexing for the left
array but 0-based for right
, which can be confusing and lead to errors.
Common mistake:
# Incorrect: mixing up the indexing
for i in range(n):
if nums[i]:
left[i] = left[i - 1] + 1 # Wrong! This would access left[-1] when i=0
Solution: Be consistent with indexing or clearly document the offset:
# Correct: using enumerate with start=1 for clarity
for i, num in enumerate(nums, 1):
if num == 1:
left[i] = left[i - 1] + 1
3. Not Initializing Arrays with Correct Size
Incorrect:
left = [0] * n # Wrong size! right = [0] * n
This would cause an index out of bounds error when accessing right[n]
or when building the left
array.
Solution: Always use size n+1
for both arrays to accommodate the boundary conditions:
left = [0] * (n + 1) right = [0] * (n + 1)
4. Alternative Sliding Window Approach Pitfall
Some might attempt a sliding window approach tracking zeros, but forget to handle the edge case where there are no zeros in the array:
Problematic sliding window:
def longestSubarray(self, nums):
zero_count = 0
left = 0
max_len = 0
for right in range(len(nums)):
if nums[right] == 0:
zero_count += 1
while zero_count > 1: # Allow at most 1 zero
if nums[left] == 0:
zero_count -= 1
left += 1
max_len = max(max_len, right - left + 1)
return max_len - 1 # Subtract 1 for the deletion
Why it fails: For nums = [1,1,1]
with no zeros, this would return 3 - 1 = 2
, which is correct. However, the logic is unclear and the -1
at the end seems arbitrary. A better approach would explicitly check if any zeros exist:
# Better: explicitly handle the all-1s case
if 0 not in nums:
return len(nums) - 1
# ... rest of sliding window logic
Which algorithm should you use to find a node that is close to the root of the 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
https assets algo monster cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in the window The window
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
Want a Structured Path to Master System Design Too? Don’t Miss This!