Lesson 12 — Sliding Window I: The Shrinking Rule

~20 minutes · sliding window · Python · Day 9

Tonight's problem is Longest Substring Without Repeating Characters. The ask sounds small: find the longest contiguous piece of a string where no character appears twice. Interviewers like it because the slow solution is easy to describe, but the fast solution depends on one new habit: keep one live window, and repair it only when it breaks.

The invariant

Grow the window on the right greedily; shrink from the left only until it is valid again. Here valid = no repeated character.

The slow way first

A brute-force version starts a fresh scan at every possible left edge. For each left edge, it grows right until a repeated character appears:

def longest_unique_slow(s):
    best = 0
    for left in range(len(s)):
        seen = set()
        for right in range(left, len(s)):
            if s[right] in seen: break
            seen.add(s[right])
            best = max(best, right - left + 1)
    return best

That is O(n²): every new left rebuilds work the previous scan mostly knew. Python sets make x in seen average O(1), but restarting the scan still repeats too many checks (Python wiki: time complexity, Python set docs).

The window way

Keep one window: s[left:right + 1]. Move right forward one character at a time. If the new character is already inside the window, the window is invalid, so move left forward until that duplicate is gone. Only then update the answer.

Key rule: update best only after the window is valid 1. grow right a b b a left right valid: a,b are unique 2. next b breaks it a b b a right invalid: b repeats do not update best yet 3. shrink left a b b a while b is still inside, keep moving left

The shaded span is the current window: grow right, then keep moving left until the duplicate is gone.

def length_of_longest_substring(s):
    left = 0
    seen = set()                         # chars inside the current window
    best = 0

    for right, ch in enumerate(s):
        while ch in seen:                # invalid: ch is repeated
            seen.remove(s[left])         # shrink from the left
            left += 1
        seen.add(ch)                     # valid again
        best = max(best, right - left + 1)
    return best

The important word is while, not if. In "abba", the second b is fixed by removing a and then the first b. Shrinking once is not enough.

Transfer: same shape, new score

Maximum Erasure Value: find the maximum sum of a contiguous subarray with no repeated values.

Before reading the reveal: what stays the same, and what changes? Say the invariant out loud first.

Your prediction first — then reveal.

The shrinking rule is the same. The validity rule is still “no repeated item in the window.” The score changes: instead of tracking window length, carry a running sum. Add the right value when it enters; subtract the left value when it leaves; update best_sum only after the window is valid again.

What the trade buys you

ApproachTimeSpace
Brute force (restart a set at each left)O(n²)O(n)
Sliding window (reuse one live set)O(n)O(n)

Each character enters the set once and leaves the set at most once. That is the whole O(n) argument.

Practice — answer before you scroll past

1. What must be true before updating best?

2. Spot the bug in this window update for s = "abba":

if ch in seen:
    seen.remove(s[left])
    left += 1
seen.add(ch)

3. Which task asks for this shrinking rule?

4. When do you move left?

Your move (tonight)

Solve Longest Substring Without Repeating Characters in your Notion tracker using the cycle — predict → attempt → compare → encode.

  1. Predict: state the validity rule: no repeated character in the current window.
  2. Attempt: code the sliding-window version from memory before watching anything.
  3. Compare: check where your reasoning differed from the source below.
  4. Encode: write the invariant in your own words in Notes.
One primary source

Watch NeetCode — Longest Substring Without Repeating Characters after your attempt, as the compare step. Listen for exactly when he decides to move the left pointer.


Stuck or curious? Ask your teacher (the agent) — that's what it's for.