INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW
01
00/12
01 / COVER STEP 10 · SLIDING WINDOW
INVARIANT · STEP 10 · COMPLETE STEP
SLIDE THE WINDOW

A nested-loop scan over every subarray is O(n²). Two pointers make it O(n): a left and a right edge that only ever move forward, enclosing a window that grows and shrinks. This deck teaches the four templates — a constant window, the longest window under a condition, counting subarrays with an exact property, and the shortest covering window — each animated on a live L/R track you can step and predict, and each wrapped around its own lecture: prime it, watch it, drill it, then solve the problem it unlocks.

12Problems
4Templates
13Units
14Live windows
← → ↑ ↓  or  W A S D  navigate SPACE next   DOUBLE-CLICK to advance I index   G goto problem   P predict H hide solutions   T close video   F fullscreen
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
02 / START HERE READ THIS ONE FIRST

HOW THIS DECK WORKS

This is not a list of problems. It is 13 learning units, and every one runs the same four beats. Go in order and the problems stop feeling random.

01 INTROWhat the concept is, and what to watch for
02 VIDEOThe lecture, full-width in theatre mode
03 DRILLS2–4 questions checking the lecture landed
04 PROBLEMSThe sheet problems that concept unlocks

Moving around

← ↑Back a slide — or A / W → ↓Forward — or D / S / Space 2×clickDouble-click the right side to advance, left to go back. A single click never moves the deck. IThe index: every problem, clickable, with your progress GJump straight to a problem by its number FFullscreen

While you study

HHide solutions — blurs code and steps so you try first PPredict mode: call the next step before the animation plays it TClose the video — Esc works too ☐ ★Mark solved, or star to revisit. Both are saved automatically.

12 PROBLEMS · EVERY ONE LINKS TO LEETCODE TO SOLVE

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
03 / INDEX PRESS I FROM ANYWHERE

INDEX — ALL 12 PROBLEMS

Click any problem to jump straight to it. Solved ones turn green. Press I from anywhere to come back here. The number is the sheet's row, not your position — the deck teaches in the order that builds, so you will meet them slightly out of numerical sequence.

CONSTANT WINDOW · 01
LONGEST WINDOW · 05
COUNTING SUBARRAYS · 04
SHORTEST WINDOW · 02
SOLVED HAS A LEETCODE LINK
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
04 / SIGNALS WHEN YOU SEE X, REACH FOR Y

WHICH WINDOW, AND WHY

Sliding window is four templates wearing different problems. The cards are the phrases in a statement that pick the template — “longest such that”, “at most k”, “count exactly”, “smallest containing” — before you write a line.

“LONGEST / SHORTEST SUBARRAY OR SUBSTRING SUCH THAT …”

a variable window: grow the right edge, shrink the left when the condition breaks

SLIDING WINDOW (LONGEST / SHORTEST)O(n)
“AT MOST K …” (k zeros, k distinct, k odd numbers)

grow R; while the count exceeds k, shrink L — the window stays valid

LONGEST VARIABLE WINDOWO(n)
“COUNT SUBARRAYS WITH EXACTLY K …”

you can't count exactly directly — subtract two at-most passes

atMost(k) − atMost(k−1)O(n)
“MAXIMUM SUM / AVERAGE OF A WINDOW OF SIZE K”

the width is fixed, so just slide: add the entering cell, drop the leaving one

CONSTANT WINDOWO(n)
“SMALLEST WINDOW CONTAINING ALL OF T”

expand R until valid, then contract L as far as it stays valid

SHORTEST COVERING WINDOWO(n)
“PAIR IN A SORTED ARRAY” / “FROM BOTH ENDS”

two pointers walking inward — move the one that can improve the answer

OPPOSITE-ENDS TWO POINTERSO(n)
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
05 / CONSTRAINTS BEFORE YOU WRITE A LINE

READ THE ANSWER OFF THE CONSTRAINTS

Sliding window lives at n ≤ 10⁵–10⁷: an O(n) scan where each of the two pointers crosses the array once. If the bound already permits O(n²) you can brute-force every subarray — but the window is what makes the big inputs pass.

n ≤
BUDGET
WHAT THAT BUYS YOU
n ≤ 20
O(2ⁿ)
enumerate every subset/subsequence — not a window problem
n ≤ 500
O(n³)
check every subarray AND scan it — the truly naive triple loop
n ≤ 5000
O(n²)
every (L,R) pair with an O(1) running update — brute-force windows
n ≤ 10⁵
O(n) · O(n log n)
sliding window / two pointers — both edges move forward once each. THE home row for this topic
n ≤ 10⁷
O(n)
a single linear pass — the window scan scales straight through

CONTIGUOUS SUBARRAY + n ≥ 10⁵ ⇒ TWO POINTERS / SLIDING WINDOW, O(n)

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
06 / ROADMAP WHAT YOU ARE ABOUT TO DO

THE WHOLE RUN, 13 UNITS

Thirteen units. Unit 1 lays out the four templates on the live track; then one lecture per problem, grouped constant → longest → counting → shortest. The last unit (Minimum Window Subsequence) goes a step beyond the sheet with no lecture.

UNIT 01

The Four Templates

▶ 36:553 DRILLSNO SHEET ROW
UNIT 02

Maximum Points from Cards

▶ 11:132 DRILLS1 PROBLEM
UNIT 03

Longest Substring, No Repeats

▶ 23:092 DRILLS1 PROBLEM
UNIT 04

Max Consecutive Ones III

▶ 29:582 DRILLS1 PROBLEM
UNIT 05

Fruit Into Baskets

▶ 30:022 DRILLS1 PROBLEM
UNIT 06

At Most K Distinct

▶ 21:322 DRILLS1 PROBLEM
UNIT 07

Substrings With All Three

▶ 19:402 DRILLS1 PROBLEM
UNIT 08

Character Replacement

▶ 25:212 DRILLS1 PROBLEM
UNIT 09

Binary Subarrays With Sum

▶ 20:272 DRILLS1 PROBLEM
UNIT 10

Nice Subarrays

▶ 4:542 DRILLS1 PROBLEM
UNIT 11

K Different Integers

▶ 20:492 DRILLS1 PROBLEM
UNIT 12

Minimum Window Substring

▶ 27:062 DRILLS1 PROBLEM
BEYOND THE SHEETUNIT 13

Minimum Window Subsequence

NO LECTURE2 DRILLS1 PROBLEM
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
07 / WARMUP LOAD THE TWO-POINTER INSTINCT

WHY IT'S O(n), AND WHICH TEMPLATE FITS

DRILL 01 · RECALL

A sliding-window loop looks like a nested loop (an outer R, an inner while that moves L). Why is it O(n), not O(n²)?

Both pointers are monotonic — they never move backward. R advances n times total, and L also only advances, so across the entire run L moves at most n steps in total — not n steps per R. Amortised, each element is added once and removed at most once, giving O(n). This 'the inner loop doesn't reset' argument is the whole reason two pointers beats the quadratic brute force.

DRILL 02 · RECALL

Which window template do you reach for: “longest subarray containing at most 2 distinct integers”?

Longest variable window with an 'at most k' shrink condition. The width isn't fixed and you want the longest valid one, so you grow R greedily and only shrink L when the distinct-count exceeds 2. That's exactly Fruit Into Baskets (k=2). If the question had said “count subarrays with exactly 2 distinct”, you'd switch to the atMost subtraction instead — recognising which of the four templates fits is the skill this deck drills.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
08 / INTRO UNIT 01 · The Four Templates

UNIT 01 — The Four Templates

Almost every sliding-window problem is one of four templates, and the hardest part is recognising which. (1) Constant window — a fixed width that slides, reusing the sum. (2) Longest window — grow the right edge, shrink the left when a condition breaks, keep the best length. (3) Counting — count subarrays with an exact property as atMost(k) − atMost(k−1). (4) Shortest window — expand to valid, then contract to minimal. In all four, both pointers only move forward, which is what turns an O(n²) subarray scan into O(n).

THE QUESTION THIS LECTURE ANSWERS

WHICH OF THE FOUR WINDOW TEMPLATES DOES THIS PROBLEM WANT?

window [L,R]grow R / shrink Lconstant · longest · counting · shortestatMost(k)O(n) amortised
WHAT TO WATCH FOR
  • 01BOTH POINTERS ONLY MOVE FORWARD → O(n), NOT O(n²)
  • 02FIXED WIDTH? → CONSTANT WINDOW (ADD NEW, DROP OLD)
  • 03'LONGEST … SUCH THAT'? → GROW R, SHRINK L ON BREAK
  • 04'COUNT EXACTLY k'? → atMost(k) − atMost(k−1) · 'SMALLEST COVERING'? → EXPAND THEN CONTRACT
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
09 / VIDEO UNIT 01 · The Four Templates

L1. Introduction to Sliding Window and 2 Pointers | Templates | Patterns

STRIVER A2Z
The Four Templates
RUNTIME 36:55
AFTER THIS → 3 DRILLS · CONCEPT UNIT
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
10 / DRILL UNIT 01 · The Four Templates · 1 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

What is the single property of L and R that makes the whole family O(n)?

Monotonic pointers — neither ever moves backward. Even though the code has an inner while that moves L, that loop doesn't restart from 0 for each R; L resumes where it left off. So the total number of L-moves over the whole scan is at most n, and the same for R — amortised O(n). This is the one insight that separates two pointers from the quadratic double loop.

DRILL 02 · RECALL

“Maximum sum of any fixed window of size k” — which template, and what's the key optimisation?

Constant window with an incremental sum. Consecutive size-k windows overlap in k−1 cells, so recomputing the sum from scratch wastes that overlap. Add the new right element and drop the old left element in O(1) per slide. That's the template behind Maximum Points from Cards (where the 'window' is the un-taken middle) and any max-average-subarray question.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
11 / DRILL UNIT 01 · The Four Templates · 2 OF 2

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · TRANSFER

“How many subarrays have exactly 3 distinct integers?” Which template, and why not count it directly?

The at-most subtraction. A single sliding window can count subarrays with at most k distinct (add R−L+1 per step), but 'exactly k' has no clean per-window count. The identity exactly(k) = atMost(k) − atMost(k−1) turns one hard count into two easy ones. This is literally Subarrays with K Different Integers, and the same shape counts Binary Subarrays With Sum and Nice Subarrays.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
12 / MECHANISM UNIT 01 · FIXED · CODE MIRRORED

CONSTANT WINDOW — ADD THE NEW, DROP THE OLD

The simplest window: a fixed width that just slides. The naive scan recomputes each window's sum from scratch — O(n·k). The trick is that consecutive windows overlap in all but two cells, so when the window steps right you add the entering element and subtract the leaving one: sum += a[R] − a[R−k]. One pass, O(n). Watch the gold cell enter on the right and the red cell leave on the left — that single swap is the whole idea, and it's the pattern behind Maximum Points from Cards.

THE WINDOW
STATE
CODE MIRROR
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
13 / MECHANISM UNIT 01 · LONGEST · CODE MIRRORED

LONGEST WINDOW — GROW R, SHRINK L WHEN IT BREAKS

The workhorse template. The right edge grows greedily, pulling in one element per step; the moment the window violates its condition (here, a repeated character), the left edge shrinks until the window is valid again. Both pointers only ever move forward, so the whole scan is O(n) even though it looks like a nested loop. Track the best length as you go. Every “longest substring/subarray such that …” problem — no repeats, at most k zeros, at most k distinct — is this one shape with a different validity test.

THE WINDOW
STATE
CODE MIRROR
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
14 / INTRO UNIT 02 · Maximum Points from Cards

UNIT 02 — Maximum Points from Cards

Maximum Points from Cards looks like it needs choices from both ends, but flip it: you take exactly k cards total from the two ends, which means the cards you leave behind form one contiguous block of size n − k in the middle. Maximising what you take is the same as minimising that leftover block — a constant-size window of width n − k. So slide a fixed window of size n − k, find its minimum sum, and the answer is total − minWindow.

THE QUESTION THIS LECTURE ANSWERS

HOW DO YOU MAXIMISE CARDS FROM BOTH ENDS WITHOUT TRYING EVERY SPLIT?

take k from endsleftover = fixed windown − k widthtotal − minWindowconstant window
WHAT TO WATCH FOR
  • 01TAKING k FROM THE ENDS LEAVES A CONTIGUOUS MIDDLE OF SIZE n − k
  • 02MAX TAKEN = TOTAL − MIN SUM OF THAT MIDDLE WINDOW
  • 03SO IT'S A CONSTANT WINDOW OF WIDTH n − k, SLID ONCE
  • 04EDGE CASE: k == n MEANS TAKE EVERYTHING (EMPTY MIDDLE)
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
15 / VIDEO UNIT 02 · Maximum Points from Cards

L2. Maximum Points You Can Obtain from Cards

STRIVER A2Z
Maximum Points from Cards
RUNTIME 11:13
AFTER THIS → 2 DRILLS · PROBLEM #01
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
16 / DRILL UNIT 02 · Maximum Points from Cards

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Why does “take k cards from the two ends” reduce to a single fixed-size window in the middle?

The untaken cards are always a contiguous middle block. If you take i from the front and k−i from the back, the leftovers are exactly the cards from index i to n−(k−i)−1 — a window of width n−k. As i ranges over its choices, that window slides across the array. Maximising the taken sum equals minimising this fixed window's sum, so answer = total − minWindowSum.

DRILL 02 · TRACE

Cards [1, 2, 3, 4, 5, 1], k = 3. Using total − (min sum of a size-(n−k) window), what is the max score?

10. Total = 16, n − k = 3. The size-3 windows are [1,2,3]=6, [2,3,4]=9, [3,4,5]=12, [4,5,1]=10; the smallest leftover is 6, so the most you can take is 16 − 6 = 10 (leave [1,2,3], take [4,5,1] from the back). A greedy 'grab the bigger end each time' would instead take 1 then 1… and get it wrong — which is exactly why the leftover-window reframing matters.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
17 / MECHANISM UNIT 02 · U02 · CODE MIRRORED

THE WINDOW IS THE PART YOU DO NOT TAKE

Taking k cards from the two ends always leaves a contiguous block of n−k in the middle, so the choice has a second reading: maximising your hand is minimising that block. That turns a problem about two ends into a fixed-size window over one span — unit 01's first template, applied to the complement of the answer rather than the answer.

THE WINDOW
STATE
CODE MIRROR
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
18 / PROBLEM #01 · CONSTANT · MED

Maximum Points You Can Obtain from Cards

MED constant ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Take exactly k cards from either end to maximise the score.” 'From both ends' + 'exactly k taken' is the tell to look at the fixed leftover window in the middle.

INTUITION

Instead of choosing how many to take from each end, note the untaken cards form a contiguous block of size n − k. Minimise that block's sum with a constant-size sliding window, and the answer is total − minWindowSum. If k == n, take everything.

STEPS
  1. total = sum of all cards; windowSize = n − k
  2. If windowSize == 0, return total (take every card)
  3. Compute the first window sum (indices 0 .. n−k−1)
  4. Slide it to the end, tracking the minimum window sum
  5. Return total − minWindowSum
BRUTEO(k²) — try every front/back split
OPTIMALO(n)
↕ SCROLL
// Max taken from ends = total − min sum of the middle window (size n−k).
int maxScore(vector<int>& cards, int k) {
    int n = cards.size(), total = 0;
    for (int x : cards) total += x;
    int windowSize = n - k;
    if (windowSize == 0) return total;          // take all cards
    int sum = 0;
    for (int i = 0; i < windowSize; i++) sum += cards[i];
    int minWindow = sum;
    for (int R = windowSize; R < n; R++) {
        sum += cards[R] - cards[R - windowSize]; // slide the fixed window
        minWindow = min(minWindow, sum);
    }
    return total - minWindow;
}
TIMEO(n)one linear pass to slide the fixed window
SPACEO(1)a couple of running sums
TRAP

Simulating the greedy 'take the bigger end' choice. Greedily grabbing the larger of the two ends is wrong — a small card now can guard a big one later. The reframing to total − min fixed window sidesteps all of that. Also handle k == n (window size 0 → take everything) or the loop indexes out of range.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
19 / INTRO UNIT 03 · Longest Substring, No Repeats

UNIT 03 — Longest Substring, No Repeats

The archetype of the longest variable window. Grow the right edge one character at a time; keep a set (or a last-seen map) of what's in the window. When s[R] is already inside, the window has a repeat — shrink L until it's gone. With a last-seen index map you can jump L straight to lastSeen[s[R]] + 1 instead of stepping it. Track the best length throughout.

THE QUESTION THIS LECTURE ANSWERS

HOW LONG IS THE LONGEST STRETCH WITH NO REPEATED CHARACTER?

longest windowlast-seen mapL jumps forwardno-repeat invariantO(n)
WHAT TO WATCH FOR
  • 01GROW R · IF s[R] IS ALREADY IN THE WINDOW, THERE'S A REPEAT
  • 02SHRINK L PAST THE PREVIOUS OCCURRENCE OF s[R]
  • 03A last-seen MAP LETS L JUMP DIRECTLY, NOT STEP ONE-BY-ONE
  • 04best = max(best, R − L + 1) AFTER THE WINDOW IS CLEAN
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
20 / VIDEO UNIT 03 · Longest Substring, No Repeats

L3. Longest Substring Without Repeating Characters

STRIVER A2Z
Longest Substring, No Repeats
RUNTIME 23:09
AFTER THIS → 2 DRILLS · PROBLEM #02
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
21 / DRILL UNIT 03 · Longest Substring, No Repeats

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

With a last-seen index map, when s[R] was seen before at index j (inside the window), where should L move to?

L = max(L, lastSeen[s[R]] + 1). Moving L to one past the previous occurrence removes exactly the duplicate. The max(L, …) is essential: an old occurrence of s[R] might sit to the left of the current L, and you must never drag L backward (that would re-admit already-dropped characters and break the monotonic-pointer guarantee).

DRILL 02 · TRACE

For s = "abcba", what is the longest substring without repeating characters?

3. Growing R: "a","ab","abc" (len 3). At R=3 the second 'b' repeats (last at 1), so L jumps to 2 → window "cb". At R=4 'a' was last at 0, which is outside the window (L=2), so no shrink → "cba" (len 3). Best = 3. The mechanism slide runs exactly this string.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
22 / MECHANISM UNIT 03 · U03 · CODE MIRRORED

SHRINK ONLY WHILE INVALID, NEVER OTHERWISE

The archetype for the longest template, on data unit 01 does not use. R only ever advances; L advances only while the window is broken and never retreats. That is the whole reason the scan is O(n) and not O(n²) — each index is entered once by R and left once by L, no matter how the two interleave.

THE WINDOW
STATE
CODE MIRROR
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
23 / PROBLEM #02 · LONGEST · MED

Longest Substring Without Repeating Characters

MED longest ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

Longest substring with no repeating character.” 'Longest … such that' over a contiguous run is the longest variable window.

INTUITION

Grow the right edge; keep the last-seen index of each character. When s[R] has been seen at index j ≥ L, a duplicate is in the window, so jump L to j + 1. Record the width R − L + 1 at every step.

STEPS
  1. L = 0, best = 0, lastSeen = {}
  2. For R from 0 to n−1:
  3. If s[R] in lastSeen and lastSeen[s[R]] >= L: L = lastSeen[s[R]] + 1
  4. lastSeen[s[R]] = R
  5. best = max(best, R − L + 1); return best
BRUTEO(n²) — check every substring for repeats
OPTIMALO(n)
↕ SCROLL
// Longest window with no repeat; L jumps past the last occurrence.
int lengthOfLongestSubstring(string s) {
    unordered_map<char, int> last;
    int L = 0, best = 0;
    for (int R = 0; R < (int)s.size(); R++) {
        auto it = last.find(s[R]);
        if (it != last.end() && it->second >= L)
            L = it->second + 1;          // jump past the duplicate
        last[s[R]] = R;
        best = max(best, R - L + 1);
    }
    return best;
}
TIMEO(n)each pointer crosses the string once
SPACEO(min(n, Σ))a map of the ≤ alphabet-size characters in the window
TRAP

Dragging L backward. A previous occurrence of s[R] may lie to the left of the current L; only jump when lastSeen[s[R]] ≥ L (or use L = max(L, lastSeen[s[R]]+1)). Moving L back re-admits dropped characters and breaks the O(n) guarantee, silently inflating the answer.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
24 / INTRO UNIT 04 · Max Consecutive Ones III

UNIT 04 — Max Consecutive Ones III

Max Consecutive Ones III is the longest window template with a budget: you may flip up to k zeros to ones, so you want the longest window containing at most k zeros. Grow R, count the zeros inside; the moment the count exceeds k, shrink L until it drops back to k. The answer is the largest window width you ever hold valid.

THE QUESTION THIS LECTURE ANSWERS

LONGEST RUN OF 1s IF YOU MAY FLIP AT MOST k ZEROS?

flip ≤ k zerosat most k zeroszero countershrink on overflowwindow width
WHAT TO WATCH FOR
  • 01REFRAME: LONGEST WINDOW WITH AT MOST k ZEROS INSIDE
  • 02GROW R · IF a[R] IS 0, zeros++
  • 03WHILE zeros > k, SHRINK L (IF a[L] WAS 0, zeros−−)
  • 04best = max(best, R − L + 1) EACH VALID STEP
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
25 / VIDEO UNIT 04 · Max Consecutive Ones III

L4. Max Consecutive Ones III

STRIVER A2Z
Max Consecutive Ones III
RUNTIME 29:58
AFTER THIS → 2 DRILLS · PROBLEM #03
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
26 / DRILL UNIT 04 · Max Consecutive Ones III

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Why reframe “flip at most k zeros to ones” as “longest window with at most k zeros”?

A window with ≤ k zeros is achievable as all ones. You have a budget of k flips, so any contiguous stretch containing at most k zeros can be made entirely ones — its whole length counts. The task becomes: find the longest window whose zero-count never exceeds k. You track just the zero count, not the actual flips, which is what keeps it O(n).

DRILL 02 · RECALL

When the zero count exceeds k, why shrink with a while and only decrement the counter when the leaving element is a zero?

Only a leaving zero reduces the count. As L advances you decrement zeros exactly when a[L] == 0; skipping that guard would under-count and shrink too far. Here removing one zero restores validity, so a single shrink suffices, but writing it as a while (zeros > k) is the safe general form that also covers templates where several elements must leave.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
27 / MECHANISM UNIT 04 · U04 · CODE MIRRORED

THE VALIDITY TEST BECOMES A BUDGET

Same template, and the only thing that changed is valid(): a window is legal while it holds at most k zeros. Note what the code never does — nothing is actually flipped. The array is untouched and the budget is the entire state, which is what makes this a window problem rather than a search over which zeros to change.

THE WINDOW
STATE
CODE MIRROR
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
28 / PROBLEM #03 · LONGEST · MED

Max Consecutive Ones III

MED longest ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Flip at most k zeros; longest run of 1s.” A budget of flips over a contiguous run is the at-most-k longest window.

INTUITION

A window is achievable as all-ones iff it holds at most k zeros. Grow R, counting zeros; whenever the count exceeds k, shrink L (decrementing the count when a zero leaves). The largest valid width is the answer.

STEPS
  1. L = 0, zeros = 0, best = 0
  2. For R from 0 to n−1: if nums[R] == 0, zeros++
  3. While zeros > k: if nums[L] == 0, zeros−−; L++
  4. best = max(best, R − L + 1)
  5. Return best
BRUTEO(n²)
OPTIMALO(n)
↕ SCROLL
// Longest window containing at most k zeros.
int longestOnes(vector<int>& nums, int k) {
    int L = 0, zeros = 0, best = 0;
    for (int R = 0; R < (int)nums.size(); R++) {
        if (nums[R] == 0) zeros++;
        while (zeros > k) {              // too many zeros -> shrink
            if (nums[L] == 0) zeros--;
            L++;
        }
        best = max(best, R - L + 1);
    }
    return best;
}
TIMEO(n)both pointers sweep once
SPACEO(1)two counters
TRAP

Decrementing zeros for every left move. Only a leaving zero reduces the count — guard with if (nums[L] == 0). And update best after the shrink loop, not during it, or you count an invalid (too many zeros) window.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
29 / INTRO UNIT 05 · Fruit Into Baskets

UNIT 05 — Fruit Into Baskets

Fruit Into Baskets is a story wrapped around longest subarray with at most 2 distinct values. Two baskets each hold one fruit type; walking left to right you collect a contiguous run using at most two types. Grow R, track the count of each type in a frequency map; when a third distinct type appears, shrink L, dropping counts, until only two types remain. The answer is the longest valid window — it's exactly “at most k distinct” with k = 2.

THE QUESTION THIS LECTURE ANSWERS

LONGEST CONTIGUOUS RUN USING AT MOST 2 FRUIT TYPES?

at most 2 distinctfrequency mapdistinct = map sizedrop to zerok = 2
WHAT TO WATCH FOR
  • 01TWO BASKETS = AT MOST 2 DISTINCT VALUES IN THE WINDOW
  • 02FREQUENCY MAP · A TYPE LEAVES THE WINDOW WHEN ITS COUNT HITS 0
  • 03WHILE map.size() > 2, SHRINK L AND DECREMENT COUNTS
  • 04IT IS THE k = 2 CASE OF AT-MOST-k-DISTINCT (NEXT UNIT)
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
30 / VIDEO UNIT 05 · Fruit Into Baskets

L5. Fruit Into Baskets

STRIVER A2Z
Fruit Into Baskets
RUNTIME 30:02
AFTER THIS → 2 DRILLS · PROBLEM #04
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
31 / DRILL UNIT 05 · Fruit Into Baskets

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Using a frequency map for the window, how do you know the number of distinct fruit types, and when does a type truly leave the window?

Distinct = keys with positive count; a type leaves at count 0. The map's size is the distinct count. When L advances you decrement map[a[L]], and only when it reaches 0 do you erase the key and reduce the distinct count. Forgetting the zero-erase leaves a phantom type in the map, so the window thinks it has too many types and shrinks incorrectly — a classic stale-map bug.

DRILL 02 · TRACE

Fruits [1, 2, 1, 2, 3]. Longest run with at most 2 distinct types?

4. [1,2,1,2] uses only types 1 and 2 — width 4. When the 3 at index 4 enters, the window holds 3 distinct types, so L shrinks past the earlier fruits until only two types remain ([2,3]). Best stays 4. This is at-most-2-distinct, the seed of the general k-distinct template.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
32 / MECHANISM UNIT 05 · U05 · CODE MIRRORED

TWO BASKETS IS TWO DISTINCT TYPES

The story is fruit and baskets; the constraint is at most two distinct values in the window. Watch the shrink carefully: dropping one copy of a fruit does not remove that type — the count has to reach zero before the distinct total falls. That is the step this template is most often got wrong on, and unit 06 is this run with the 2 lifted out.

THE WINDOW
STATE
CODE MIRROR
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
33 / PROBLEM #04 · LONGEST · MED

Fruit Into Baskets

MED longest ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“At most two basket types; longest collectable run.” 'At most 2 distinct' over a contiguous run is the at-most-k-distinct window with k = 2.

INTUITION

Slide a window keeping a frequency map of fruit types. Grow R; when the map holds more than 2 distinct types, shrink L, decrementing counts and erasing a type when its count hits 0. Track the longest valid width.

STEPS
  1. L = 0, best = 0, count = {}
  2. For R from 0 to n−1: count[fruits[R]]++
  3. While count has > 2 keys: count[fruits[L]]−−; erase if 0; L++
  4. best = max(best, R − L + 1)
  5. Return best
BRUTEO(n²)
OPTIMALO(n)
↕ SCROLL
// Longest window with at most 2 distinct values (k=2).
int totalFruit(vector<int>& fruits) {
    unordered_map<int, int> count;
    int L = 0, best = 0;
    for (int R = 0; R < (int)fruits.size(); R++) {
        count[fruits[R]]++;
        while ((int)count.size() > 2) {  // a third type -> shrink
            if (--count[fruits[L]] == 0) count.erase(fruits[L]);
            L++;
        }
        best = max(best, R - L + 1);
    }
    return best;
}
TIMEO(n)each pointer moves forward once; map ops are O(1)
SPACEO(1)a map of at most 3 keys
TRAP

Not erasing a key when its count hits 0. The distinct count is the map's size; if you leave zero-count keys behind, size() overstates the distinct types and the window shrinks when it shouldn't. This is the exact at-most-k-distinct template with k = 2 — the next problem just parameterises the 2.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
34 / INTRO UNIT 06 · At Most K Distinct

UNIT 06 — At Most K Distinct

At Most K Distinct is Fruit Into Baskets generalised to k: the longest substring containing at most k distinct characters. Identical machinery — a frequency map, grow R, and while the map holds more than k distinct keys, shrink L. The only change from Fruit is the literal 2 becomes the parameter k. Seeing that one problem is another with a constant swapped is half of pattern recognition.

THE QUESTION THIS LECTURE ANSWERS

LONGEST SUBSTRING WITH AT MOST k DISTINCT CHARACTERS?

at most k distinctgeneralises Fruitmap.size() > kone constant changedO(n)
WHAT TO WATCH FOR
  • 01SAME AS FRUIT, BUT k DISTINCT INSTEAD OF 2
  • 02FREQUENCY MAP · SHRINK WHILE map.size() > k
  • 03DROP A KEY WHEN ITS COUNT REACHES 0
  • 04best = max(best, R − L + 1) ON EVERY VALID WINDOW
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
35 / VIDEO UNIT 06 · At Most K Distinct

L6. Longest Substring With At Most K Distinct Characters

STRIVER A2Z
At Most K Distinct
RUNTIME 21:32
AFTER THIS → 2 DRILLS · PROBLEM #05
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
36 / DRILL UNIT 06 · At Most K Distinct

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

What is the only substantive difference between the Fruit Into Baskets solution and At Most K Distinct?

Fruit is literally k = 2. The shrink guard becomes while (map.size() > k) and that's the whole change. Recognising that a 'new' problem is an old one with a constant turned into a parameter is a core interview skill — it means you already have the code. (This problem is LeetCode premium, but the pattern is the same free one you drilled on Fruit.)

DRILL 02 · RECALL

For counting-style variants (exactly k distinct), how does this at-most-k window get reused?

The same window, switched from 'max length' to 'count subarrays', is atMost. Keep the identical grow/shrink-on-> k structure, but instead of best = max(...) do count += R−L+1. Then exactly(k) = atMost(k) − atMost(k−1) — which is precisely how Subarrays with K Different Integers is solved later in the deck.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
37 / MECHANISM UNIT 06 · U06 · CODE MIRRORED

THE SAME RUN WITH THE CONSTANT LIFTED OUT

Unit 05 with its 2 replaced by k, which is worth seeing as one motion rather than two problems. The template, the shrink condition and the bookkeeping are identical; only the number differs. Recognising that the fruit-basket problem was never about fruit is the transfer this unit tests.

THE WINDOW
STATE
CODE MIRROR
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
38 / PROBLEM #05 · LONGEST · HARD

Longest Substring with At Most K Distinct Characters

HARD longest ▶ SOLVE ON LEETCODE PREMIUM
SIGNAL — WHAT GIVES IT AWAY

“Longest substring with at most k distinct characters.” The general form of Fruit Into Baskets — at-most-k-distinct window.

INTUITION

Identical to Fruit with the 2 replaced by k: a frequency map, grow R, and while the map holds more than k distinct keys, shrink L (erasing a key at count 0). Track the longest valid width.

STEPS
  1. L = 0, best = 0, count = {}
  2. For R from 0 to n−1: count[s[R]]++
  3. While count has > k keys: count[s[L]]−−; erase if 0; L++
  4. best = max(best, R − L + 1)
  5. Return best
BRUTEO(n²)
OPTIMALO(n)
↕ SCROLL
// Longest window with at most k distinct characters.
int lengthOfLongestSubstringKDistinct(string s, int k) {
    unordered_map<char, int> count;
    int L = 0, best = 0;
    for (int R = 0; R < (int)s.size(); R++) {
        count[s[R]]++;
        while ((int)count.size() > k) {  // more than k distinct -> shrink
            if (--count[s[L]] == 0) count.erase(s[L]);
            L++;
        }
        best = max(best, R - L + 1);
    }
    return best;
}
TIMEO(n)both pointers cross once; map ops O(1)
SPACEO(k)a map of at most k+1 keys
TRAP

Treating it as a new problem. It is Fruit Into Baskets with the constant 2 turned into the parameter k — same map, same forward pointers, same zero-erase. Edge case: k == 0 should return 0. (LeetCode premium, but the pattern is the free one you already drilled.)

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
39 / INTRO UNIT 07 · Substrings With All Three

UNIT 07 — Substrings With All Three

Number of Substrings Containing All Three flips from 'longest' to counting, with a slick trick. For each right index R, track the last seen index of a, b and c. Once all three have appeared, every substring that starts at or before the minimum of those three last-seen indices and ends at R contains all three — that's 1 + min(lastA, lastB, lastC) substrings ending at R. Sum over all R.

THE QUESTION THIS LECTURE ANSWERS

HOW MANY SUBSTRINGS CONTAIN AT LEAST ONE a, ONE b AND ONE c?

count substringslast-seen indexmin of the three1 + minsum over R
WHAT TO WATCH FOR
  • 01TRACK last[a], last[b], last[c] — THE MOST RECENT INDEX OF EACH
  • 02ONCE ALL THREE ≥ 0, THE WINDOW [0..R] CONTAINS ALL THREE
  • 03VALID STARTS ENDING AT R = 1 + min(last[a],last[b],last[c])
  • 04ADD THAT TO THE COUNT FOR EVERY R
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
40 / VIDEO UNIT 07 · Substrings With All Three

L7. Number of Substrings Containing All Three Characters

STRIVER A2Z
Substrings With All Three
RUNTIME 19:40
AFTER THIS → 2 DRILLS · PROBLEM #06
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
41 / DRILL UNIT 07 · Substrings With All Three

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Why is the number of valid substrings ending at R equal to 1 + min(last[a], last[b], last[c])?

The binding constraint is the rarest-recent character. To contain all three, the substring's start must be ≤ every character's most recent index — so ≤ the minimum of last[a], last[b], last[c]. Start positions 0, 1, …, min all work: that's min + 1 substrings ending at R. Summing across R counts every qualifying substring exactly once, in O(n).

DRILL 02 · TRACE

For s = "abcabc" (indices 0–5), at R = 3 (s[3]='a') the last-seen indices are a→3, b→1, c→2. How many valid substrings end at R = 3?

2. min(3,1,2) = 1, so starts 0 and 1 qualify: "abca" and "bca" each contain all three. Start 2 ("ca") is missing a b, which matches the cut-off at min = 1. The count is 1 + min = 2.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
42 / MECHANISM UNIT 07 · U07 · CODE MIRRORED

NO WINDOW AT ALL — THREE INDICES ARE THE STATE

This one is filed with the window problems and is not one. For each R, the substrings ending there that contain all three letters are exactly those starting at or before the earliest of the three last-seen positions, so the count is min(last) + 1. Nothing shrinks, nothing is maintained, and the rarest letter is what bounds the answer.

THE WINDOW
STATE
CODE MIRROR
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
43 / PROBLEM #06 · COUNTING · MED

Number of Substrings Containing All Three Characters

MED counting ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

Count substrings containing at least one a, one b and one c.” 'Count substrings with a coverage property' is a counting window (last-seen flavour).

INTUITION

For each right index, track the last-seen index of a, b and c. Once all three have appeared, every substring ending at R whose start is at or before min(lastA, lastB, lastC) contains all three — that's 1 + min(...) substrings. Sum over all R.

STEPS
  1. last = {a:-1, b:-1, c:-1}, total = 0
  2. For R from 0 to n−1: last[s[R]] = R
  3. m = min(last[a], last[b], last[c])
  4. If m >= 0: total += m + 1 (valid starts 0..m)
  5. Return total
BRUTEO(n²) — test every substring
OPTIMALO(n)
↕ SCROLL
// For each R, valid starts = 1 + min(last a, last b, last c).
int numberOfSubstrings(string s) {
    int last[3] = {-1, -1, -1};
    long total = 0;
    for (int R = 0; R < (int)s.size(); R++) {
        last[s[R] - 'a'] = R;
        int m = min({last[0], last[1], last[2]});
        if (m >= 0) total += m + 1;      // starts 0..m all contain a,b,c
    }
    return (int)total;
}
TIMEO(n)one pass, three last-seen indices
SPACEO(1)three integers
TRAP

Adding R + 1 instead of min + 1. The number of valid start positions is bounded by the rarest-recent character — the minimum of the three last-seen indices — not by R. Also only add once all three have been seen (min ≥ 0); before that, no substring qualifies.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
44 / INTRO UNIT 08 · Character Replacement

UNIT 08 — Character Replacement

Longest Repeating Character Replacement is a longest window with a subtle validity test. You may replace up to k characters, so a window is valid when (window length − count of its most frequent character) ≤ k — the non-majority characters are the ones you'd replace. Grow R, keep a frequency map and the running maxFreq; when (R − L + 1) − maxFreq > k, shrink L. The best window width is the answer.

THE QUESTION THIS LECTURE ANSWERS

LONGEST WINDOW MADE UNIFORM BY REPLACING AT MOST k CHARACTERS?

windowLen − maxFreq ≤ kreplace the minoritymaxFreqstale-max tricklongest window
WHAT TO WATCH FOR
  • 01VALID WHEN windowLen − maxFreq ≤ k (REPLACE THE MINORITY)
  • 02TRACK A FREQUENCY MAP AND THE RUNNING maxFreq
  • 03IF windowLen − maxFreq > k, SHRINK L (DECREMENT ITS COUNT)
  • 04maxFreq NEED NOT DECREASE ON SHRINK — THE ANSWER STILL HOLDS
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
45 / VIDEO UNIT 08 · Character Replacement

L8. Longest Repeating Character Replacement

STRIVER A2Z
Character Replacement
RUNTIME 25:21
AFTER THIS → 2 DRILLS · PROBLEM #07
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
46 / DRILL UNIT 08 · Character Replacement

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

What makes a window valid, i.e. convertible to all-same-character with at most k replacements?

windowLen − maxFreq ≤ k. Keep the most frequent character as-is; everything else must be replaced, and there are windowLen − maxFreq of those. If that's within your budget k, the whole window can be made uniform. You maximise the window under this constraint.

DRILL 02 · RECALL

A well-known optimisation: maxFreq is never decreased even when the window shrinks. Why is the final answer still correct?

A stale (too-high) maxFreq can't manufacture a larger valid window than one already seen. The window width only increases when a genuine maxFreq supported it; keeping an old maxFreq merely means the window doesn't shrink as eagerly, but best is only updated to the current width, which never exceeds a legitimately-achieved one. So skipping the (expensive) maxFreq recompute on shrink is safe — a classic and initially-surprising optimisation.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
47 / MECHANISM UNIT 08 · U08 · CODE MIRRORED

maxFreq IS NEVER RECOMPUTED, AND THAT IS SAFE

A window is valid when everything that is not the commonest letter fits the replacement budget: (R−L+1) − maxFreq ≤ k. The part worth pausing on is what the code leaves out — maxFreq is not recalculated when the window shrinks. A stale maximum can only make a window look worse than it is, so it may cost an unnecessary shrink but can never produce a wrong answer.

THE WINDOW
STATE
CODE MIRROR
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
48 / PROBLEM #07 · LONGEST · MED

Longest Repeating Character Replacement

MED longest ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Replace at most k characters; longest same-letter run.” 'Longest window fixable within a budget' is the longest window with a maxFreq test.

INTUITION

A window is valid when the characters you'd replace — everything but the most frequent one — number at most k: (R − L + 1) − maxFreq ≤ k. Grow R, update the frequency map and maxFreq; if the window becomes invalid, move L once. Track the best width.

STEPS
  1. L = 0, best = 0, maxFreq = 0, count = {}
  2. For R: count[s[R]]++; maxFreq = max(maxFreq, count[s[R]])
  3. If (R − L + 1) − maxFreq > k: count[s[L]]−−; L++
  4. best = max(best, R − L + 1)
  5. Return best
BRUTEO(26·n²) — try every window and letter
OPTIMALO(n)
↕ SCROLL
// Valid while windowLen - maxFreq <= k; maxFreq kept (not lowered).
int characterReplacement(string s, int k) {
    int count[26] = {0}, L = 0, best = 0, maxFreq = 0;
    for (int R = 0; R < (int)s.size(); R++) {
        maxFreq = max(maxFreq, ++count[s[R] - 'A']);
        if ((R - L + 1) - maxFreq > k) { // must replace too many -> shrink one
            count[s[L] - 'A']--;
            L++;
        }
        best = max(best, R - L + 1);
    }
    return best;
}
TIMEO(n)one pass; maxFreq never recomputed on shrink
SPACEO(1)a 26-slot frequency array
TRAP

Recomputing maxFreq on every shrink (or shrinking with a while). Because the answer only grows, a stale-high maxFreq can't inflate best beyond a genuinely-achieved width, so a single if-shrink and a never-lowered maxFreq are correct and faster. Recomputing is safe but unnecessary; using if keeps the window from ever shrinking below the best.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
49 / INTRO UNIT 09 · Binary Subarrays With Sum

UNIT 09 — Binary Subarrays With Sum

Binary Subarrays With Sum is the first pure counting problem, and it introduces the identity the whole group rests on: exactly(goal) = atMost(goal) − atMost(goal−1). atMost(x) is a plain variable window on a 0/1 array — grow R adding to the running sum, shrink L while the sum exceeds x, and add R − L + 1 (every subarray ending at R) to the count. Run it twice and subtract.

THE QUESTION THIS LECTURE ANSWERS

HOW MANY SUBARRAYS SUM TO EXACTLY goal — WITHOUT COUNTING EXACTLY?

exactly = atMost − atMostcount += R−L+10/1 arraytwo passesprefix-sum alt
WHAT TO WATCH FOR
  • 01exactly(goal) = atMost(goal) − atMost(goal − 1)
  • 02atMost(x): SHRINK L WHILE sum > x, THEN count += R − L + 1
  • 03R − L + 1 = EVERY SUBARRAY ENDING AT R (they all have sum ≤ x)
  • 04TWO PASSES, ONE SUBTRACTION — O(n)
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
50 / VIDEO UNIT 09 · Binary Subarrays With Sum

L9. Binary Subarrays With Sum

STRIVER A2Z
Binary Subarrays With Sum
RUNTIME 20:27
AFTER THIS → 2 DRILLS · PROBLEM #08
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
51 / DRILL UNIT 09 · Binary Subarrays With Sum

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

In atMost(x), once the window [L,R] has sum ≤ x, why do you add exactly R − L + 1 to the count?

They're the subarrays ending at R. If [L,R] is valid, so is every [i,R] for L ≤ i ≤ R (a shorter subarray has a smaller-or-equal sum on a non-negative array). That's R − L + 1 new valid subarrays, all ending at R — counting them per step, without double-counting, gives atMost(x) in one pass.

DRILL 02 · RECALL

Why can't you count “exactly goal” with a single sliding window directly?

'Exactly' doesn't compose the way 'at most' does. The R − L + 1 trick works because every shorter suffix of a valid at-most window is also valid. For 'exactly goal' that's false — trim one element and the sum drops below goal. So you express the exact count as the difference of two monotone at-most counts, each of which does compose. (A prefix-sum + hashmap count is the other standard route.)

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
52 / MECHANISM UNIT 09 · COUNTING · CODE MIRRORED

COUNTING — atMost(k), AND count += R − L + 1

Counting subarrays with an exact property is deceptively hard directly, so flip it: exactly(k) = atMost(k) − atMost(k−1). And atMost is a plain variable window with one beautiful line: when the window [L,R] is valid, every subarray ending at R and starting anywhere from L to R is valid too — that's R − L + 1 new subarrays, added in one step. Watch the count jump by the window width each time R advances. Binary Subarrays With Sum, Nice Subarrays and K Different Integers are all this subtraction.

THE WINDOW
STATE
CODE MIRROR
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
53 / PROBLEM #08 · COUNTING · MED

Binary Subarrays With Sum

MED counting ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

Count subarrays with sum exactly goal” on a 0/1 array. 'Count subarrays with an exact sum' is the atMost subtraction.

INTUITION

You can't cleanly count 'exactly goal' with one window, so use exactly(goal) = atMost(goal) − atMost(goal−1). Each atMost(x) is a variable window: grow R adding to the sum, shrink L while the sum exceeds x, and add R − L + 1 per step.

STEPS
  1. Define atMost(x): if x < 0 return 0
  2. L = 0, sum = 0, count = 0
  3. For R: sum += nums[R]; while sum > x: sum −= nums[L++]
  4. count += R − L + 1
  5. Return atMost(goal) − atMost(goal − 1)
BRUTEO(n²)
OPTIMALO(n)
↕ SCROLL
// exactly(goal) = atMost(goal) - atMost(goal-1) on a 0/1 array.
int atMost(vector<int>& a, int x) {
    if (x < 0) return 0;
    int L = 0, sum = 0, count = 0;
    for (int R = 0; R < (int)a.size(); R++) {
        sum += a[R];
        while (sum > x) sum -= a[L++];   // shrink until sum <= x
        count += R - L + 1;              // subarrays ending at R
    }
    return count;
}
int numSubarraysWithSum(vector<int>& a, int goal) {
    return atMost(a, goal) - atMost(a, goal - 1);
}
TIMEO(n)two linear at-most passes
SPACEO(1)a few counters
TRAP

Forgetting the atMost(goal−1) pass, or the x < 0 guard. atMost(goal) alone counts too many; the subtraction isolates 'exactly'. And atMost(−1) must return 0 (when goal == 0) or you over-subtract. A prefix-sum + hashmap counting method is the equally valid alternative.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
54 / INTRO UNIT 10 · Nice Subarrays

UNIT 10 — Nice Subarrays

Count Nice Subarrays asks for subarrays with exactly k odd numbers — and it is Binary Subarrays With Sum in disguise. Treat each odd number as a 1 and each even as a 0; then 'exactly k odds' becomes 'sum exactly k', solved by the very same atMost(k) − atMost(k−1). The only change from the previous problem is what you add to the running sum.

THE QUESTION THIS LECTURE ANSWERS

HOW MANY SUBARRAYS CONTAIN EXACTLY k ODD NUMBERS?

odd = 1exactly k = atMost k − atMost k−1parity mapreuse Binary SubarraysO(n)
WHAT TO WATCH FOR
  • 01ODD → 1, EVEN → 0 · 'k ODDS' BECOMES 'SUM = k'
  • 02REUSE atMost(k) − atMost(k − 1) UNCHANGED
  • 03atMost ADDS (nums[R] % 2) TO THE WINDOW SUM
  • 04SAME O(n) TWO-PASS SUBTRACTION
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
55 / VIDEO UNIT 10 · Nice Subarrays

L10. Count Number of Nice Subarrays

STRIVER A2Z
Nice Subarrays
RUNTIME 4:54
AFTER THIS → 2 DRILLS · PROBLEM #09
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
56 / DRILL UNIT 10 · Nice Subarrays

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

What single change turns the Binary Subarrays With Sum solution into Count Nice Subarrays?

Just relabel odds as 1s. The property 'contains exactly k odd numbers' is 'the parity-array sum equals k'. Feed nums[i] % 2 into the same atMost window and subtract — no new algorithm. Recognising this reuse is the entire point of grouping these problems together.

DRILL 02 · RECALL

Both Binary Subarrays and Nice Subarrays are 0/1 counting problems. What's the other classic way to count 'subarrays with sum k' on such arrays?

Prefix sum + hash map. Keep a running prefix sum and a map of how many times each prefix value has occurred; the number of subarrays ending at R with sum k is the count of prefix value prefix − k seen so far. It's a good alternative to atMost − atMost and generalises to arrays with negatives, where the monotone-window trick breaks.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
57 / MECHANISM UNIT 10 · U10 · CODE MIRRORED

EXACTLY-k HAS NO WINDOW. atMost DOES

There is no window whose validity is exactly three odds — growing R can jump straight past it. So the problem is rewritten: atMost(k) − atMost(k−1), and each of those is an ordinary shrink-while-invalid run. This slide is the first of the two calls; the answer is the difference between them.

THE WINDOW
STATE
CODE MIRROR
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
58 / PROBLEM #09 · COUNTING · MED

Count Number of Nice Subarrays

MED counting ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Count subarrays with exactly k odd numbers.” 'Exactly k of a property' → the atMost subtraction, with odds mapped to 1s.

INTUITION

Treat each odd number as 1 and even as 0; then 'exactly k odds' is 'sum exactly k', identical to Binary Subarrays With Sum. Compute atMost(k) − atMost(k−1) where atMost counts subarrays whose odd-count is ≤ x.

STEPS
  1. Define atMost(x): if x < 0 return 0
  2. L = 0, odds = 0, count = 0
  3. For R: odds += nums[R] % 2; while odds > x: odds −= nums[L++] % 2
  4. count += R − L + 1
  5. Return atMost(k) − atMost(k − 1)
BRUTEO(n²)
OPTIMALO(n)
-// exactly(goal) = atMost(goal) - atMost(goal-1) on a 0/1 array.+// Odd -> 1, so exactly k odds = atMost(k) - atMost(k-1). int atMost(vector<int>& a, int x) {     if (x < 0) return 0;     int L = 0, sum = 0, count = 0;     for (int R = 0; R < (int)a.size(); R++) {-        sum += a[R];-        while (sum > x) sum -= a[L++];   // shrink until sum <= x+        sum += a[R] & 1;+        while (sum > x) sum -= a[L++] & 1;   // shrink until #odds <= x         count += R - L + 1;              // subarrays ending at R     }     return count; }-int numSubarraysWithSum(vector<int>& a, int goal) {-    return atMost(a, goal) - atMost(a, goal - 1);+int numberOfSubarrays(vector<int>& a, int k) {+    return atMost(a, k) - atMost(a, k - 1); }
TIMEO(n)two at-most passes over the parity array
SPACEO(1)counters only
TRAP

Over-thinking it. This is Binary Subarrays With Sum with a[i] & 1 in place of a[i] — the same two-pass subtraction. The usual slip is a fresh, buggy 'exactly' window instead of reusing the proven atMost − atMost. Prefix-sum-of-parities + hashmap also works.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
59 / INTRO UNIT 11 · K Different Integers

UNIT 11 — K Different Integers

Subarrays with K Different Integers is the counting group's hardest, and its cleanest demonstration of the identity: exactly k distinct = atMost(k) − atMost(k−1). Here atMost(x) is the at-most-x-distinct counting window from Unit 6 — grow R, shrink L while the distinct count exceeds x, and add R − L + 1. Two such passes, subtracted, give the exact-distinct count in O(n).

THE QUESTION THIS LECTURE ANSWERS

HOW MANY SUBARRAYS HAVE EXACTLY k DISTINCT INTEGERS?

exactly = atMost − atMostdistinct-count windowcount += R−L+1hard but reusedO(n)
WHAT TO WATCH FOR
  • 01exactly-k-distinct = atMost(k) − atMost(k − 1)
  • 02atMost(x): FREQUENCY MAP, SHRINK WHILE distinct > x
  • 03ADD R − L + 1 PER STEP (SUBARRAYS ENDING AT R)
  • 04THE SAME at-most-k-distinct WINDOW FROM UNIT 6, NOW COUNTING
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
60 / VIDEO UNIT 11 · K Different Integers

L11. Subarray with k different integers

STRIVER A2Z
K Different Integers
RUNTIME 20:49
AFTER THIS → 2 DRILLS · PROBLEM #10
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
61 / DRILL UNIT 11 · K Different Integers

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

This 'Hard' problem reuses which earlier window, and with what modification?

The at-most-k-distinct window, in counting mode, run twice. You already built the map-based distinct window for Fruit / K Distinct. Replace best = max(...) with count += R − L + 1 to get atMost(x), then compute atMost(k) − atMost(k−1). A 'Hard' rating that dissolves into two things you've already drilled — that's the payoff of learning the templates.

DRILL 02 · RECALL

Why is atMost(k) − atMost(k−1) exactly the count of subarrays with exactly k distinct?

Set subtraction on nested conditions. The subarrays with at most k distinct split cleanly into those with at most k−1 and those with exactly k. So exactly(k) = atMost(k) − atMost(k−1) — exact, not approximate. The same reasoning underlies every 'exactly k' counting problem in this deck.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
62 / MECHANISM UNIT 11 · U11 · CODE MIRRORED

THE SAME SUBTRACTION, A DIFFERENT TEST

Identical to unit 10 with one line changed: the validity test counts distinct values instead of odd ones. Everything else — the shrink, the count += R−L+1, the second call with k−1 — is untouched. Learning this as a template rather than as a problem is what makes the second one free.

THE WINDOW
STATE
CODE MIRROR
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
63 / PROBLEM #10 · COUNTING · HARD

Subarrays with K Different Integers

HARD counting ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

“Count subarrays with exactly k distinct integers” (Hard). 'Exactly k distinct' → atMost(k) − atMost(k−1) on the distinct-count window.

INTUITION

Reuse the at-most-k-distinct window from Fruit / K Distinct, but in counting mode: for each R, shrink L while the window holds more than x distinct values, then add R − L + 1. That's atMost(x); the answer is atMost(k) − atMost(k−1).

STEPS
  1. Define atMost(x): frequency map, L = 0, count = 0
  2. For R: add nums[R]; while map.size() > x: drop nums[L], erase at 0, L++
  3. count += R − L + 1
  4. Return atMost(k) − atMost(k − 1)
BRUTEO(n²)
OPTIMALO(n)
↕ SCROLL
// exactly k distinct = atMost(k) - atMost(k-1), counting window.
int atMost(vector<int>& a, int x) {
    unordered_map<int, int> cnt;
    int L = 0, count = 0;
    for (int R = 0; R < (int)a.size(); R++) {
        cnt[a[R]]++;
        while ((int)cnt.size() > x) {           // > x distinct -> shrink
            if (--cnt[a[L]] == 0) cnt.erase(a[L]);
            L++;
        }
        count += R - L + 1;
    }
    return count;
}
int subarraysWithKDistinct(vector<int>& a, int k) {
    return atMost(a, k) - atMost(a, k - 1);
}
TIMEO(n)two at-most-distinct passes, each O(n)
SPACEO(n)a frequency map up to n keys
TRAP

Trying to count exactly-k distinct in one pass. There's no clean per-R count for 'exactly', so the direct attempt double-counts or misses. The whole trick is the subtraction of two at-most-distinct windows — the same window you built for Fruit, switched from max-length to counting. Remember to erase zero-count keys so size() is the true distinct count.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
64 / INTRO UNIT 12 · Minimum Window Substring

UNIT 12 — Minimum Window Substring

Minimum Window Substring is the shortest covering window — the hardest problem in the deck, and just the window template run in reverse. You want the smallest window of s that contains all characters of t (with multiplicity). Expand R until the window is valid, then contract L as far as it stays valid, recording the minimum length each time. A need/have counter makes validity an O(1) check.

THE QUESTION THIS LECTURE ANSWERS

WHAT IS THE SMALLEST WINDOW OF s CONTAINING ALL OF t?

shortest windowexpand then contractneed / haveformed == requiredrecord on shrink
WHAT TO WATCH FOR
  • 01EXPAND R UNTIL EVERY REQUIRED CHARACTER IS COVERED (formed == required)
  • 02THEN CONTRACT L WHILE STILL VALID, RECORDING THE MIN LENGTH
  • 03need[c] COUNTS · have TRACKS HOW MANY REQUIREMENTS ARE FULLY MET
  • 04MINIMUMS APPEAR WHILE SHRINKING, NEVER WHILE GROWING
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
65 / VIDEO UNIT 12 · Minimum Window Substring

L12. Minimum Window Substring

STRIVER A2Z
Minimum Window Substring
RUNTIME 27:06
AFTER THIS → 2 DRILLS · PROBLEM #11
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
66 / DRILL UNIT 12 · Minimum Window Substring

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

How does the need/have bookkeeping decide, in O(1), whether the window currently covers t?

Count fully-satisfied requirements, not characters. required is the number of distinct characters in t. Each time a character's window-count reaches its need, increment have; when it drops below, decrement. The window covers t exactly when have == required — an O(1) test you maintain incrementally as R and L move.

DRILL 02 · RECALL

During the contract phase, when do you stop moving L?

Stop when the next removal breaks validity. While the window stays valid you keep shrinking and recording the (shrinking) length; as soon as dropping s[L] would take some character below its required count, have falls below required and you stop, going back to growing R. The smallest window for each right endpoint is captured right before that break.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
67 / MECHANISM UNIT 12 · SHORTEST · CODE MIRRORED

SHORTEST WINDOW — EXPAND TO VALID, THEN CONTRACT

The mirror image of the longest template. Here you want the smallest window that covers a requirement (contains all of t). So grow the right edge until the window becomes valid, then contract the left edge as far as it stays valid, recording the minimum length each time it's valid. When shrinking finally breaks validity, resume growing R. A need/have counter tells you validity in O(1). It's the exact shape of Minimum Window Substring — the hardest problem in the deck, and just this loop with bookkeeping.

THE WINDOW
STATE
CODE MIRROR
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
68 / PROBLEM #11 · SHORTEST · HARD

Minimum Window Substring

HARD shortest ▶ SOLVE ON LEETCODE
SIGNAL — WHAT GIVES IT AWAY

Smallest window of s containing all of t” (Hard). 'Minimum window covering a requirement' is the shortest window template.

INTUITION

Expand R until the window contains every character of t (with multiplicity), tracked by a need/have counter. Once valid, contract L as far as it stays valid, recording the minimum length. Resume expanding when it breaks.

STEPS
  1. Build need[c] from t; required = distinct chars in t; have = 0
  2. For R: include s[R]; if its count reaches need, have++
  3. While have == required: update best; remove s[L]; if it drops below need, have--; L++
  4. Track the best window's start and length
  5. Return the best window (or "" if none)
BRUTEO(|s|² · |t|)
OPTIMALO(|s| + |t|)
↕ SCROLL
// Expand to valid, contract to minimal; need/have tracks coverage.
string minWindow(string s, string t) {
    if (t.empty() || s.size() < t.size()) return "";
    unordered_map<char, int> need;
    for (char c : t) need[c]++;
    int required = need.size(), have = 0;
    int L = 0, bestLen = INT_MAX, bestL = 0;
    unordered_map<char, int> win;
    for (int R = 0; R < (int)s.size(); R++) {
        char c = s[R];
        win[c]++;
        if (need.count(c) && win[c] == need[c]) have++;
        while (have == required) {                 // valid -> shrink
            if (R - L + 1 < bestLen) { bestLen = R - L + 1; bestL = L; }
            char d = s[L];
            win[d]--;
            if (need.count(d) && win[d] < need[d]) have--;
            L++;
        }
    }
    return bestLen == INT_MAX ? "" : s.substr(bestL, bestLen);
}
TIMEO(|s| + |t|)each pointer crosses s once; validity is O(1)
SPACEO(|Σ|)frequency maps over the alphabet
TRAP

Recording the answer while expanding, or comparing counts wrong. The minimum windows appear only while contracting — update best inside the while have == required loop. Increment have exactly when a count reaches its need (==, not ) and decrement when it drops below, or the validity check drifts.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
69 / INTRO UNIT 13 · Minimum Window Subsequence

UNIT 13 — Minimum Window Subsequence

Minimum Window Subsequence is the twist ending, and where two pointers meets a little DP. You want the smallest window of s that contains t as a subsequence (same order, not necessarily contiguous) — so the frequency-count trick of Minimum Window Substring no longer applies; order matters. The classic O(n·m) approach: sweep s, and whenever you finish matching t forward, walk backward to find the tightest start for that end. It has no lecture in this playlist — the drills carry it.

THE QUESTION THIS LECTURE ANSWERS

SMALLEST WINDOW OF s THAT CONTAINS t AS A SUBSEQUENCE (ORDER MATTERS)?

subsequence windoworder mattersforward then backwardtwo-pointer + DPbeyond the sheet
WHAT TO WATCH FOR
  • 01SUBSEQUENCE, NOT SUBSTRING — ORDER MATTERS, GAPS ALLOWED
  • 02FREQUENCY COUNTING FAILS HERE; YOU MUST MATCH t IN ORDER
  • 03FORWARD MATCH t; ON COMPLETION, WALK BACK TO TIGHTEN THE START
  • 04TRACK THE SHORTEST (end − start) OVER ALL COMPLETIONS · O(n·m)
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
70 / DRILL UNIT 13 · Minimum Window Subsequence

CHECK — CAN YOU ANSWER IT WITHOUT LOOKING BACK?

DRILL 01 · RECALL

Why does the Minimum Window Substring approach (need/have frequency counts) fail for Minimum Window Subsequence?

Counts don't encode order. Minimum Window Substring only needs enough of each character anywhere in the window. A subsequence needs t's letters to appear in order (with gaps allowed), which a multiset can't check. So you match t against s positionally — advance a pointer in t only when the current s character equals it — rather than counting frequencies.

DRILL 02 · RECALL

After a forward pass matches all of t ending at some index e in s, what does the backward walk accomplish?

It tightens the start for a fixed end. A forward match tells you an end index where t is complete, but the start it used may be unnecessarily early. Walking backward from e, re-matching t in reverse, snaps the start as late as possible, giving the minimum-length window ending at e. Sweeping all such ends and keeping the shortest is the O(n·m) solution; a DP table over (i in s, j in t) is the other standard route.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
71 / MECHANISM UNIT 13 · U13 · CODE MIRRORED

THE ANSWER IS A SUBSEQUENCE, SO THE WINDOW CANNOT BE MAINTAINED

Unit 12 kept a window valid incrementally. That is impossible here, because the letters need not touch — so this walks forward to match t in order, and then backward from the landing point, matching t in reverse, to pull the left edge in as far as it will go. The backward pass is the entire difference, and it exists because a subsequence has slack a substring does not.

THE WINDOW
STATE
CODE MIRROR
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
72 / PROBLEM #12 · SHORTEST · HARD

Minimum Window Subsequence

HARD shortest ▶ SOLVE ON LEETCODE PREMIUM
SIGNAL — WHAT GIVES IT AWAY

“Smallest window of s with t as a subsequence” (Hard, premium). 'Subsequence, order matters' rules out frequency counting — it's a positional two-pointer / DP.

INTUITION

Sweep s matching t forward one character at a time; each time you finish t, you have an end index. Walk backward from there, re-matching t in reverse, to snap the start as late as possible — that's the tightest window ending there. Keep the shortest across all completions.

STEPS
  1. i = 0 (index in s), best length = ∞
  2. Scan s with a pointer j over t; advance j when s[i] == t[j]
  3. When j reaches end of t: an end is found — walk backward to find the latest start
  4. Update best with (end − start + 1); resume just after that start
  5. Return the best window, or "" if t never completes
BRUTEO(|s|² · |t|)
OPTIMALO(|s| · |t|)
↕ SCROLL
// Forward-match t, then walk back to tighten the start.
string minWindow(string s, string t) {
    int n = s.size(), m = t.size(), bestLen = INT_MAX, bestStart = -1;
    int i = 0;
    while (i < n) {
        int j = 0;
        while (i < n) {                        // forward match t
            if (s[i] == t[j]) j++;
            if (j == m) break;
            i++;
        }
        if (j < m) break;                      // t not completed
        int end = i;
        j = m - 1;
        while (j >= 0) {                       // walk back to latest start
            if (s[i] == t[j]) j--;
            i--;
        }
        i++;                                   // i is now the tightest start
        if (end - i + 1 < bestLen) { bestLen = end - i + 1; bestStart = i; }
        i++;                                   // resume just past this start
    }
    return bestStart < 0 ? "" : s.substr(bestStart, bestLen);
}
TIMEO(|s| · |t|)forward-then-backward sweep, or a DP table over (i, j)
SPACEO(1) two-pointer / O(|s|·|t|) DPO(1) for the two-pointer sweep
TRAP

Reusing the Minimum Window Substring frequency counts. A subsequence needs t's characters in order, which multiset counts can't verify — you must match positionally. The forward-then-backward sweep is the neat O(n·m) way; the backward walk is what makes each window minimal. A DP over (i in s, j in t) is the alternative. (LeetCode premium.)

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
73 / RECALL RETRIEVAL, NOT RECOGNITION

NAME THE TEMPLATE FROM THE STATEMENT

DRILL 01 · TRANSFER

Match the phrasing to the template: “count subarrays with exactly K odd numbers”.

Counting via the at-most subtraction. You cannot cleanly count “exactly K” in one window pass, but “at most K” is a standard variable window (shrink while the odd-count exceeds K, add R−L+1 each step). Then exactly(K) = atMost(K) − atMost(K−1). That's Count Nice Subarrays verbatim (odds treated as 1s), and the identical trick solves Binary Subarrays With Sum and Subarrays with K Different Integers.

DRILL 02 · RECALL

In the shortest-window template (Minimum Window Substring), when do you record a candidate answer — while expanding R, or while contracting L?

While contracting L. Expanding R only ever makes the window bigger, so the smallest windows appear while you're shrinking. The pattern is: grow R until valid, then contract L and record the length on every step that remains valid; when a contraction finally breaks validity, go back to growing R. Recording during expansion would only ever capture over-long windows.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
74 / TRAPS PLAUSIBLE, WRONG, AND SILENT

SIX WAYS TO BE PLAUSIBLY WRONG

Window bugs pass the sample and fail the judge: an if that should be a while, a best updated too early, an exactly counted directly, a stale frequency map. Every one returns a believable number.

SHRINKING WITH if INSTEAD OF while

When the window becomes invalid you often must move L more than once. An if shrinks a single step and can leave the window still invalid; use a while so L advances until validity is restored. (The one exception is the O(n) 'never shrink below best' longest-window trick, which deliberately uses a single if.)

UPDATING best BEFORE THE WINDOW IS VALID

For a longest-window problem, record R−L+1 only after the shrink loop has restored validity. Recording mid-shrink counts an invalid window and overstates the answer — a classic silent off-by-one.

COUNTING exactly(k) DIRECTLY

Trying to count subarrays with exactly k in a single window doesn't compose — the same left boundary can serve many rights ambiguously. Always go through atMost(k) − atMost(k−1). Forgetting the k−1 pass is the usual bug, and it returns a plausible-but-wrong count.

FORGETTING count += R − L + 1 (USING +1)

In the counting template each new R contributes every subarray ending at R that starts in [L,R] — that's R−L+1, not 1. Adding 1 counts only the single-element or full window and undercounts massively.

MOVING THE WRONG POINTER IN OPPOSITE-ENDS

On a sorted array with pointers at both ends, if the sum is too small you must move L rightward (to increase it), and if too big move R leftward. Moving the wrong one walks away from the answer and can loop past it.

STALE FREQUENCY / DISTINCT COUNTS ON SHRINK

When L leaves an element you must decrement its frequency and, if it hits zero, decrement the distinct-count. Skipping the zero check leaves a phantom distinct value in the map and the window never shrinks correctly — it usually manifests as an answer that's too long.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
75 / CHEATSHEET REVISION SURFACE

ONE SCREEN, THE WHOLE DECK

Seven windows, one page. The right-hand column is the phrase in the statement that should trigger each — the night-before surface.

ALGORITHM
TIME
SPACE
REACH FOR IT WHEN
Constant window
O(n)
O(1)
fixed size k — sum += a[R] − a[R−k] as it slides
Longest variable window
O(n)
O(k)
grow R; while invalid shrink L; best = max(best, R−L+1)
Longest, never-shrink trick
O(n)
O(k)
shrink with a single if — window never gets smaller than best
Counting exactly(k)
O(n)
O(k)
atMost(k) − atMost(k−1); each atMost adds R−L+1
Shortest covering window
O(n)
O(Σ)
expand to valid, contract L while valid, record min
Opposite-ends two pointers
O(n)
O(1)
sorted array; move L up if too small, R down if too big
Window from both ends
O(n)
O(1)
take k from ends = total − min window of size n−k
INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
76 / CLOSE STEP 10 · COMPLETE STEP

SLIDE THE WINDOW

Four templates — constant, longest, counting, shortest — and a single idea underneath: two pointers that only move forward, turning an O(n²) subarray scan into O(n). Every one ran live on the L/R track, and every problem was primed, watched, drilled and applied. Next on the sheet: stacks & queues, then heaps.

00%
OF THIS DECK SOLVED
← ALL TOPICSSTEP 03 · ARRAYSSTEP 04 · BS ON ANSWERS

All 12 playlist lectures are used. Minimum Window Subsequence (premium) has no lecture in this playlist and is a lecture-less unit. Two problems (At Most K Distinct, Min Window Subsequence) are LeetCode premium — the pattern is taught in full regardless.

INVARIANT · SLIDING WINDOW · TWO POINTERS & THE WINDOW · COMPLETE STEP
■ TOO SMALL TO READ
1280×720
INVARIANT · STEP 10 · COMPLETE STEP

This one needs a laptop

Not a preference — an arithmetic one. Every slide is a fixed 1280 × 720 stage: a graph animating beside the code that drives it, with the problems laid out two columns wide. It scales as a single piece, so on a screen this size the body text comes out around 4px tall.

Shrinking it further would not help, and rebuilding it to reflow would mean losing the thing that makes it worth reading.

YOUR SCREEN0 × 0
NEEDED1060 × 610
WHAT IS WAITING ON THE LAPTOP
WATCH IT RUN, THEN RUN IT FROM MEMORY
Your progress is saved per device, so anything you tick on the laptop will be waiting there.