1004. Max Consecutive Ones III

Given a binary array and a budget of k zero-to-one flips, find the longest contiguous run of 1s you can produce. It is a variable-size sliding window that stays valid while it contains at most k zeros.

MediumSliding WindowTypeScript

PROBLEM What we're solving

You are given an array nums of only 0s and 1s and an integer k. You may flip at most k zeros to ones. Return the length of the longest contiguous subarray that is all 1s after the flips.

Example: nums=[1,1,1,0,0,0,1,1,1,1,0], k=2. Flip the two zeros at indices 4 and 5, giving 1,1,1,1,1,1 across indices 4–9 (length 6). Answer: 6.

KEY IDEA A window is valid while it holds ≤ k zeros

Insight → "Flip at most kzeros" means a subarray is achievable iff it contains at most k zeros— every zero inside is one flip you can afford. So track a single number: how many 0s are in the current window. Grow the window on the right; whenever the zero count exceeds k, shrink from the left until it is valid again. The answer is the largest valid window seen.
Classic confusion → You do not need to track 1s, and you do not need to decide whichzeros to flip. The only quantity that matters is the count of zeros inside the window. People often over-engineer this by storing positions of zeros — a single counter, incremented when a 0 enters and decremented when a 0 leaves, is enough.

RECIPE Grow right, shrink left while zeros > k

  • 0 · Initialize. L = 0, zeros = 0, best = 0.
  • 1 · Expand R. If nums[R] === 0, increment zeros— a new zero just entered the window and would cost a flip.
  • 2 · Shrink while invalid. While zeros > k, advance L; if the element leaving was a 0, decrement zeros. Stop once the window again holds ≤ k zeros.
  • 3 · Record best. best = Math.max(best, R - L + 1)— the current window is guaranteed valid here.
  • 4 · Return best.
Pattern transfer → This is the same "at most k bad items" window as Longest Repeating Character Replacement (LC 424, where the cost is windowLen − maxFreq) and Longest Subarray of 1s After Deleting One Element (LC 1493, where k = 1and you subtract 1 at the end). Whenever you see "flip / delete / replace at most k," reach for this window.

COST Complexity & alternatives

Brute force (all subarrays)
O(n²)
For every start, extend right counting zeros.
Sliding window
O(n)
Single pass; O(1) extra space.

R advances n times and L advances at most n times total across the whole run, so the inner whileis amortized O(1) — total work is O(2n) = O(n). Only three integer counters are kept, so space is O(1).

RUN IT Expand R, shrink L while the window holds more than k zeros

step 0 / 28
STARTBegin with an empty window. Expand R rightward, counting 0s inside the window. The window stays valid while zeros ≤ k (every 0 can be flipped to a 1).
1function longestOnes(nums: number[], k: number): number {
2 let L = 0;
3 let zeros = 0;
4 let best = 0;
5
6 for (let R = 0; R < nums.length; R++) {
7 if (nums[R] === 0) zeros++; // a 0 entered the window
8
9 while (zeros > k) { // too many 0s to flip
10 if (nums[L] === 0) zeros--; // a 0 is leaving on the left
11 L++;
12 }
13
14 best = Math.max(best, R - L + 1); // window now has <= k zeros
15 }
16
17 return best;
18}
nums =10111203040516171819010
State
0
L
R
[]
window
0
windowLen
0
zeros
2
k
0
best
L (left pointer)R (right pointer)inside windowL = R / too many zerosvalid / best length
slowfast

TYPESCRIPT The solution, annotated

longestOnes.ts
function longestOnes(nums: number[], k: number): number {
  let L = 0;
  let zeros = 0;
  let best = 0;

  for (let R = 0; R < nums.length; R++) {
    if (nums[R] === 0) zeros++;       // a 0 entered the window

    while (zeros > k) {               // too many 0s to flip
      if (nums[L] === 0) zeros--;     // a 0 is leaving on the left
      L++;
    }

    best = Math.max(best, R - L + 1); // window now has <= k zeros
  }

  return best;
}

Reading it block by block

Lines 2–4 — state. L is the window's left edge, zeros counts how many 0s are currently inside it, and best remembers the longest valid window seen so far.
Lines 6–7 — expand R. Each step extends the window one element to the right. If the new element is a 0, it costs a flip, so bump zeros.
Lines 9–12 — restore validity. If zeros now exceeds k, the window needs more flips than we have. Advance L; when the element that leaves is a 0, that flip is freed, so decrement zeros. The loop runs until the window is valid again.
Line 14 — record best. By now the window holds ≤ k zeros, so its full length R - L + 1 is achievable. Update best if it is the longest yet.
Complexity → O(n) time — R moves forward n times and L moves forward at most n times in total, so each index is visited at most twice. O(1) space: just the three counters L, zeros, and best.

INTERVIEWFollow-ups they'll ask

  • "What if you must flip exactly k zeros?"The longest valid window for "at most k" already covers it when there are ≥ k zeros overall; otherwise the whole array works. The "at most" formulation is the clean invariant.
  • "Return the actual subarray, not its length?" Record bestL = L whenever best updates and return nums.slice(bestL, bestL + best).
  • "How does this relate to LC 424?"Identical window; there the "bad item" count is windowLen − maxFreq, here it is simply the number of zeros. Same expand/shrink loop.
  • "What about LC 1493 (delete exactly one element)?" Run the same window with k = 1, then subtract one from the answer because a deletion (not a flip) is required.
  • "Can the window ever shrink past R?" No — each incoming 0 raises zeros by one, so the while removes at most one element per step; L never overtakes R.

OPTIMAL Sliding Window

longestOnes.ts
function longestOnes(nums: number[], k: number): number {
  let L = 0;
  let zeros = 0;
  let best = 0;

  for (let R = 0; R < nums.length; R++) {
    if (nums[R] === 0) zeros++;       // a 0 entered the window

    while (zeros > k) {               // too many 0s to flip
      if (nums[L] === 0) zeros--;     // a 0 is leaving on the left
      L++;
    }

    best = Math.max(best, R - L + 1); // window now has <= k zeros
  }

  return best;
}
Complexity → O(n) time — R moves forward n times and L moves forward at most n times in total, so each index is visited at most twice. O(1) space: just the three counters L, zeros, and best.

ALT 1 Brute force — try every subarray

O(n²) time · O(1) space

For each start index, extend to the right counting zeros; the subarray is achievable while its zero count stays ≤ k.

approach-2.ts
function longestOnes(nums: number[], k: number): number {
  let best = 0;

  for (let i = 0; i < nums.length; i++) {
    let zeros = 0;
    for (let j = i; j < nums.length; j++) {
      if (nums[j] === 0) zeros++;
      if (zeros > k) break;          // can't flip this many
      best = Math.max(best, j - i + 1);
    }
  }

  return best;
}
Note → Restarting the zero count from every index is O(n²). The sliding window keeps one window that only grows on the right and shrinks on the left, collapsing the work into a single O(n) pass.

ALT 2 Non-shrinking window (size never decreases)

O(n) time · O(1) space

A subtle variant: replace the while with an ifso the window slides as a unit and never shrinks — the final R - L is the answer.

approach-3.ts
function longestOnes(nums: number[], k: number): number {
  let L = 0;
  let zeros = 0;

  for (let R = 0; R < nums.length; R++) {
    if (nums[R] === 0) zeros++;
    if (zeros > k) {                 // slide, don't shrink
      if (nums[L] === 0) zeros--;
      L++;
    }
  }

  return nums.length - L;            // window width never shrank
}
Note → Because the window only ever moves forward (never contracts), its width R - L + 1 is monotonically non-decreasing, so the final width n - L equals the best ever seen. Same O(n), slightly slicker but less obvious than the explicit while + best version.

MNEMONIC The one-liner

"Grow right; when zeros exceed k, walk left until they don't. Best window wins."

TRIGGERS When you see ___ → reach for ___

"flip at most k zeros / bits to 1"sliding window, valid while zeros ≤ k
binary array + "longest run of 1s"count zeros in window, shrink when over budget
"at most k bad elements in a subarray"variable-size window, shrink-left invariant
"delete one element to extend a run"same window with k = 1, subtract 1 from the answer

SKELETON The reusable shape

skeleton.ts
let L = 0, zeros = 0, best = 0;

for (let R = 0; R < nums.length; R++) {
  if (nums[R] === 0) zeros++;

  while (zeros > k) {
    if (nums[L] === 0) zeros--;
    L++;
  }

  best = Math.max(best, R - L + 1);
}
return best;

FLASHCARDS Tap to flip

What makes a window valid in LC 1004?
It contains at most kzeros — each zero is a flip you can afford.
tap to flip
1 / 6
Answer to reveal explanations. No penalty for retries.Score: 0 / 9
QUESTION 01
What is the time complexity of the sliding window solution?
QUESTION 02
What condition keeps the window valid?
QUESTION 03
For nums=[1,1,1,0,0,0,1,1,1,1,0], k=2, what does the algorithm return?
QUESTION 04
Inside the while (zeros > k) loop, when do you decrement zeros?
QUESTION 05
What happens when k = 0?
QUESTION 06
Why is it unnecessary to store the positions of the zeros you flip?
QUESTION 07
Which problem uses essentially the same sliding window pattern?
QUESTION 08
#1004 · Max Consecutive Ones IIIFind the longest run of 1s you can make by flipping at most k zeros: a variable window that holds at most k zeros, shrinking from the left whenever a (k+1)th zero enters. O(n) time, O(1) space.Which algorithmic approach does this primarily use?
QUESTION 09
#1004 · Max Consecutive Ones IIIFind the longest run of 1s you can make by flipping at most k zeros: a variable window that holds at most k zeros, shrinking from the left whenever a (k+1)th zero enters. O(n) time, O(1) space.Which implementation correctly solves it?