Problem Overview
LeetCode 1004 — Max Consecutive Ones III gives you a binary array nums and an integer k. You may flip at most k zeros to ones. Return the maximum number of consecutive 1s in the resulting array.
The constraint that you can flip at most k zeros means the answer is the length of the longest contiguous subarray that contains at most k zeros. No actual flipping is needed in the algorithm — you simply find the longest window where the zero count stays within budget.
This is a classic variable-length sliding window problem on a binary array. The window is valid when it contains at most k zeros, and the goal is to maximize its length. The approach runs in O(n) time and O(1) space — no extra data structures required.
- Binary array nums, integer k; flip at most k zeros to ones
- Return the maximum number of consecutive 1s in the modified array
- Equivalent to: find the longest subarray with at most k zeros
- No actual mutation needed — track zeros inside a sliding window
- O(n) time, O(1) space using two pointers left and right
- Window is valid when zeros <= k; shrink from left when zeros > k
Reframe as Sliding Window
The key insight is to reframe the problem: instead of thinking about flipping zeros, think about finding the longest subarray that contains at most k zeros. If a window has at most k zeros, you could flip all of them to ones, turning the entire window into consecutive 1s.
This is a standard variable sliding window: maintain a left pointer and a right pointer. Expand right by one each step. When the zero count inside the window exceeds k, the window is invalid — shrink it by advancing left until the window becomes valid again. At every step, update the maximum window length seen so far.
Variable sliding windows appear whenever the problem asks for the longest subarray satisfying some monotone condition. Here the condition is "zero count <= k". Because adding elements can only increase the zero count and removing elements can only decrease it, the condition is monotone — making the sliding window approach valid.
Reframe: Flip k Zeros = Find Longest Subarray With at Most k Zeros
The flip operation is a red herring for the algorithm. Reframe "flip at most k zeros" as "find the longest subarray with at most k zeros." This converts the problem into a standard variable sliding window — one of the most recognizable patterns in array problems. Once you see it, the implementation follows directly from the window validity condition: zeros <= k.
Algorithm
Initialize left = 0, zeros = 0, maxLen = 0. Iterate right from 0 to n-1. At each step: if nums[right] == 0, increment zeros. Then while zeros > k: if nums[left] == 0, decrement zeros; increment left. After shrinking, update maxLen = max(maxLen, right - left + 1).
An alternative uses if instead of while for the shrinking step. With if, the window never shrinks below its historical maximum size. Both approaches yield the same final answer, but the if-variant is slightly simpler to implement and maintains the invariant that the window only grows or stays the same.
Both variants are O(n) time — the left pointer never moves past the right pointer, so each element is visited at most twice across the entire pass. Space is O(1): only left, zeros, and maxLen are tracked.
- 1left = 0, zeros = 0, maxLen = 0
- 2for right in range(len(nums)):
- 3 — if nums[right] == 0: zeros += 1
- 4 — while zeros > k: (or: if zeros > k)
- 5 if nums[left] == 0: zeros -= 1
- 6 left += 1
- 7 — maxLen = max(maxLen, right - left + 1)
- 8return maxLen
Why While vs If for Shrinking
Using while for the shrinking step ensures the window is always valid after each expansion: zeros <= k is restored before the maxLen update. This gives the exact maximum length at every single step of the loop — useful if you need intermediate window states, not just the final answer.
Using if shrinks the window by exactly one position each time the zero count exceeds k. The window therefore never shrinks below its historical maximum — it can only grow or stay the same size. The maxLen update at the end of each iteration captures the window size, which equals the running maximum. The final window size is the answer.
Both approaches are correct for this problem and run in O(n) time. The if-variant is a neat optimization: it avoids the inner while loop entirely, making the code a flat single loop. Interviewers often ask why if works here — the answer is that since the window never shrinks below its max, the final answer is simply right - left + 1 at the end of the loop.
The if-Variant: The Window Only Grows, Never Shrinks Below Its Max
With if instead of while, the window shifts right in lock-step — it grows when a new character fits, and slides (shifts left by one, right by one) when it does not. The window size is a non-decreasing quantity. The final window size after the loop equals the maximum window size ever seen, so right - left + 1 at the end is the answer without needing a separate maxLen variable.
Walk-Through Example
Trace nums = [1,1,1,0,0,0,1,1,1,1,0], k = 2. Initialize left = 0, zeros = 0, maxLen = 0.
right expands to index 3 (first 0): zeros = 1. right expands to index 4 (second 0): zeros = 2. Still valid (zeros == k). right expands to index 5 (third 0): zeros = 3 > k. Shrink: nums[left=0]=1, left becomes 1. zeros still 3 > k. nums[left=1]=1, left becomes 2. zeros still 3 > k. nums[left=2]=1, left becomes 3. zeros still 3 > k. nums[left=3]=0, zeros becomes 2, left becomes 4. Now valid. maxLen = max(0, 5-4+1) = 2.
right continues expanding to indices 6-10. At right=10 (last 0): zeros becomes 3 > k. Shrink: nums[left=4]=0, zeros becomes 2, left becomes 5. Window is now [5..10] = length 6. maxLen = max(previous, 6) = 6. Final answer: 6.
- 1left=0, right=0..2: all 1s, zeros=0, window grows to [0..2], maxLen=3
- 2right=3 (0): zeros=1, valid, window=[0..3], maxLen=4
- 3right=4 (0): zeros=2, valid, window=[0..4], maxLen=5
- 4right=5 (0): zeros=3 > k=2, shrink: left advances to 4 (past first 0 at index 3), zeros=2
- 5window=[4..5], maxLen remains 5
- 6right=6..9: all 1s, window grows to [4..9], maxLen=6
- 7right=10 (0): zeros=3 > k=2, shrink: left advances to 5 (past 0 at index 4), zeros=2
- 8window=[5..10] = length 6, maxLen=6 — final answer
Code Walkthrough: Python and Java
Python (while variant): def longestOnes(nums, k): left = zeros = 0; result = 0; for right in range(len(nums)): if nums[right] == 0: zeros += 1; while zeros > k: if nums[left] == 0: zeros -= 1; left += 1; result = max(result, right - left + 1); return result. O(n) time, O(1) space.
Python (if variant — same final answer): def longestOnes(nums, k): left = zeros = 0; for right in range(len(nums)): if nums[right] == 0: zeros += 1; if zeros > k: if nums[left] == 0: zeros -= 1; left += 1; return right - left + 1. No separate result variable needed — the final window size is the answer. Java follows the same structure: a single for loop on right, an if/while block on left, and a running max or final window size.
Both implementations use left and right integer pointers and a zeros counter — no arrays, queues, or maps. Time complexity is O(n) in both cases: each index is processed at most twice (once by right, once by left). Space is O(1). This is the canonical sliding window template for "longest subarray with at most k of something."
Same Pattern as Longest Repeating Character Replacement (LeetCode 424)
Max Consecutive Ones III (1004) and Longest Repeating Character Replacement (424) share the same sliding window skeleton: expand right, track a condition, shrink left when the window becomes invalid. In 1004, the condition is zeros <= k. In 424, the condition is (window length - max frequency) <= k. Once you internalize one, the other is a two-minute adaptation. Both problems appear frequently in sliding window interview rounds.