Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

15.0 Pattern Primer — The Moving Window

A sliding window is two pointers that delimit a contiguous range, plus a running state that’s updated in $O(1)$ per step instead of recomputed per range. The whole art is deciding when the left pointer moves:

Fixed-size windows

The window is always exactly k long: right advances every step, and left = right - k + 1 is derived, never chosen. The state update is add the entering element, remove the leaving one — the running sum of 15.4 is the archetype. No condition, no shrink logic; the left pointer is arithmetic.

Variable-size (shrink-until-valid) windows

Here left moves conditionally — the window must satisfy a property, and the answer is the largest (or smallest) valid window:

for right in 0..n-1:
    state.add(s[right])                        # expand
    while not valid(state):                    # the window broke the rule
        state.remove(s[left]); left++          # shrink from the left
    answer = max(answer, right - left + 1)

The monotonicity requirement: shrinking must eventually restore validity (validity is monotone — a sub-window of a valid window is valid). If the property isn’t monotone, this template breaks. Used by 15.2, 15.3, 15.5, 15.7.

The “last index” map trick

Longest Substring Without Repeating Characters stores char -> last position; a repeat pulls left directly past the previous occurrence (the 10.2 value-to-state move) — no while loop needed, because the condition is “no repeats”, which is fully determined by the last sighting.

The monotonic deque

Sliding Window Maximum needs “max of the current window” in $O(1)$ per step. The deque keeps indices with decreasing values — the front is the current max; expired indices pop from the front, smaller values pop from the back before inserting. It’s the monotonic stack idea turned into a two-ended structure with an expiry condition.

Complexity intuition

Every element enters the window once and leaves once → $O(n)$ total regardless of the inner while loops. The running state is $O(1)$ per update (a sum, a count, a deque operation). The interview tell: “contiguous subarray/substring” + “largest/smallest satisfying a condition” → sliding window, not nested loops. The nested loop is $O(n^2)$; the window is $O(n)$ because no element is processed twice.