Max Consecutive Ones III: Sliding Window with Flips
You are given a binary array nums and an integer k. You can flip at most k zeros to ones. Return the maximum number of consecutive 1s in the array after performing at most k flips. This is LeetCode 1004: Max Consecutive Ones III.
For example, given nums = [1,1,1,0,0,0,1,1,1,1,0,0] and k = 2, the answer is 6. You can flip two of the zeros to create a window of six consecutive ones.
Why this problem matters
Sliding window problems are some of the most common questions in technical interviews, and Max Consecutive Ones III is one of the best for building that skill. It teaches you how to maintain a flexible window that grows and shrinks based on a constraint (the number of flipped zeros). The same pattern appears in problems like "Longest Substring Without Repeating Characters," "Longest Repeating Character Replacement," and "Minimum Window Substring."
Once you internalize this version, you can adapt it to any problem where you need to find the longest subarray satisfying some condition that you can track with a counter.
The key insight
You do not actually flip any zeros. Instead, you maintain a sliding window and count how many zeros are inside it. As long as the zero count is <= k, the window is valid because you could flip those zeros. When the count exceeds k, you shrink the window from the left until the count drops back to k.
The problem transforms from "flip at most k zeros" into "find the longest subarray containing at most k zeros." That reframing makes the sliding window approach natural.
The algorithm:
- Initialize two pointers,
leftandright, both at 0. Trackzero_countandbest. - Expand
rightone step at a time. Ifnums[right]is 0, incrementzero_count. - While
zero_countexceedsk, shrink from the left. Ifnums[left]is 0, decrementzero_count. Moveleftforward. - Update
bestwith the current window size (right - left + 1). - After
rightpasses the end, returnbest.
The solution
def longest_ones(nums, k):
left = 0
zero_count = 0
best = 0
for right in range(len(nums)):
if nums[right] == 0:
zero_count += 1
while zero_count > k:
if nums[left] == 0:
zero_count -= 1
left += 1
best = max(best, right - left + 1)
return best
The outer for loop advances right across the entire array. Every time we include a new element, we check if it is a zero and update the count. The inner while loop fires only when we have too many zeros in the window. It moves left forward, undoing the effect of any zero that leaves the window, until the constraint is satisfied again. After each adjustment, we record the window size if it is the new maximum.
The key detail is that left never moves backward. Both pointers sweep from left to right across the array, so the total work is linear.
This "at most k" constraint pattern works for any sliding window problem. Replace "zeros" with whatever you are counting (distinct characters, negative numbers, elements exceeding a threshold) and the structure stays the same: expand right, check constraint, shrink left if violated, update best.
Visual walkthrough
Let's trace through nums = [1,1,1,0,0,0,1,1,1,1,0,0] with k = 2. Watch how the window expands when zeros are within budget and contracts when the count exceeds k. Green cells are 1s inside the window, yellow cells are flipped zeros, and red cells are zeros that have not been flipped.
Step 1: Expand right through indices 0, 1, 2. All ones so far.
Window [0..2] contains three 1s. No zeros flipped yet. Window size = 3.
Step 2: right = 3. First zero encountered. Flip it (zeros = 1).
nums[3] = 0. Flip it (zeros becomes 1, within k=2). Window [0..3], size = 4.
Step 3: right = 4. Second zero. Flip it (zeros = 2, equals k).
nums[4] = 0. Flip it (zeros becomes 2, exactly k). Window [0..4], size = 5.
Step 4: right = 5. Third zero. zeros exceeds k. Shrink left until a zero leaves.
nums[5] = 0 pushes zeros to 3. Move left past 1s at 0, 1, 2, then past the zero at 3 (zeros drops to 2). Left = 4. Window [4..5], size = 2. best stays 5.
Step 5: Expand right through indices 6, 7, 8, 9. All ones.
nums[6] through nums[9] are all 1s. Window grows without constraint. Window [4..9], size = 6. best = 6.
Step 6: right = 10. Another zero. Shrink left past the zero at index 4.
nums[10] = 0 pushes zeros to 3. Move left: nums[4] = 0 (un-flip, zeros drops to 2). Left = 5. Window [5..10], size = 6. best stays 6.
Step 7: right = 11. Another zero. Shrink left past the zero at index 5.
nums[11] = 0 pushes zeros to 3. Move left: nums[5] = 0 (un-flip, zeros drops to 2). Left = 6. Window [6..11], size = 6. best stays 6.
Done: right has passed the end of the array. Return best = 6.
The longest subarray of all 1s with at most k=2 flipped zeros has length 6.
Notice how the window reaches its maximum size of 6 at step 5, spanning indices 4 through 9. After that, every new zero forces the left pointer to advance past an old zero, keeping the window size steady at 6. The best answer never decreases.
Complexity analysis
| Approach | Time | Space |
|---|---|---|
| Sliding window | O(n) | O(1) |
Time is O(n) where n is the length of the array. Although there is a while loop inside a for loop, the left pointer only moves forward and never revisits an element. Each element is visited at most twice (once by right, once by left), so the total number of operations is at most 2n.
Space is O(1). We only use a few integer variables (left, zero_count, best). No extra data structures are needed.
The building blocks
1. Expanding the window
The core of any sliding window is the expansion step. You move the right pointer forward and update your tracking variable:
for right in range(len(nums)):
if nums[right] == 0:
zero_count += 1
This is the same in every sliding window problem. The only thing that changes is what you track. Here it is zero count. In "Longest Substring Without Repeating Characters" it is a set of characters. In "Minimum Window Substring" it is a frequency map. The expansion logic is always one line of bookkeeping after advancing right.
2. Shrinking to restore the constraint
When the window violates its constraint, you shrink from the left until it is valid again:
while zero_count > k:
if nums[left] == 0:
zero_count -= 1
left += 1
The while loop ensures you keep shrinking until the constraint holds. The undo logic inside mirrors the expansion logic: if adding a zero incremented the count, removing a zero decrements it. This symmetry between expanding and shrinking is the pattern you want to internalize. Once you can write both sides without thinking, any sliding window problem becomes a matter of plugging in the right constraint.
Edge cases
Before submitting, think through these scenarios:
- All ones:
nums = [1,1,1,1], anyk. Zero count never increases. The window spans the whole array. Returnlen(nums). - All zeros, k equals length:
nums = [0,0,0],k = 3. You flip every zero. Returnlen(nums). - All zeros, k equals 0:
nums = [0,0,0],k = 0. The window can never include a zero. Return 0. - k equals 0: The problem reduces to "Max Consecutive Ones" (LeetCode 485). No flips allowed, just find the longest run of 1s.
- k larger than the number of zeros: You can flip every zero. Return
len(nums). - Single element:
nums = [0],k = 1returns 1.nums = [1],k = 0returns 1.
From understanding to recall
You now understand the sliding window with a zero-flip budget. The logic is clean: expand right, track zeros, shrink left when over budget, record the best. But under interview pressure, small details trip people up. Do you use while or if for the shrink? Do you update best inside or outside the while? Do you check zero_count > k or zero_count >= k?
These are not conceptual mistakes. They are recall gaps. Spaced repetition is how you close them. You practice writing the expand-and-shrink loop from memory, first after a day, then after three days, then after a week. After a few rounds, the pattern flows out automatically. You see "longest subarray with at most k of something" and you write the solution without pausing to think about the details.
Related posts
- Longest Substring Without Repeating Characters - Classic sliding window for unique characters
- Minimum Window Substring - Sliding window with character frequency tracking
- Max Consecutive Ones - The simpler version without flips
CodeBricks breaks Max Consecutive Ones III into its expand-window and shrink-window building blocks, then drills them independently with spaced repetition. You type each piece from scratch until the pattern is automatic. When a sliding window problem shows up in your interview, you do not think about it. You just write it.