2026-08-14 18:37:13 -04:00
|
|
|
|
---
|
|
|
|
|
|
title: 'Sliding Window'
|
|
|
|
|
|
description: 'Two pointers plus incremental state between them.'
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-08-20 16:04:24 -04:00
|
|
|
|
## The idea
|
2026-08-19 13:05:47 -04:00
|
|
|
|
|
2026-08-20 16:04:24 -04:00
|
|
|
|
Two pointers that move the **same direction**, carrying state between them.
|
|
|
|
|
|
|
|
|
|
|
|
Think of a caterpillar:
|
|
|
|
|
|
- The head crawls forward and eats (expand right).
|
|
|
|
|
|
- When the rule breaks, the tail pulls in until the rule holds again (contract left).
|
|
|
|
|
|
|
|
|
|
|
|
The window is always a **contiguous** slice. You never rebuild it — you update the carried state as edges move. That is what makes it O(n).
|
|
|
|
|
|
|
|
|
|
|
|
**The recipe:**
|
|
|
|
|
|
1. Expand right. Add the new element to your state.
|
|
|
|
|
|
2. Rule broken? Contract left until it holds.
|
|
|
|
|
|
3. Record the best window. Repeat.
|
|
|
|
|
|
|
|
|
|
|
|
## The picture
|
|
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
|
flowchart TD
|
|
|
|
|
|
A["a b c a b c<br/>[a] window = a"] --> B["[a b] expand → ab"]
|
|
|
|
|
|
B --> C["[a b c] expand → abc, best = 3"]
|
|
|
|
|
|
C --> D["a [b c a] 'a' repeats → contract, then expand"]
|
|
|
|
|
|
D --> E["a b [c a b] keep sliding, best stays 3"]
|
|
|
|
|
|
|
|
|
|
|
|
style C fill:#2e7d32,color:#fff
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Longest Substring Without Repeating (LC 3)
|
|
|
|
|
|
|
|
|
|
|
|
State = a set of chars in the window. Repeat found? Shrink from the left until it is gone.
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
def length_of_longest_substring(s: str) -> int:
|
|
|
|
|
|
window = set()
|
|
|
|
|
|
left = 0
|
|
|
|
|
|
best = 0
|
|
|
|
|
|
|
|
|
|
|
|
for right, c in enumerate(s):
|
|
|
|
|
|
while c in window: # rule broken
|
|
|
|
|
|
window.remove(s[left]) # contract left
|
|
|
|
|
|
left += 1
|
|
|
|
|
|
window.add(c) # expand right
|
|
|
|
|
|
best = max(best, right - left + 1)
|
|
|
|
|
|
return best
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Best Time to Buy and Sell (LC 121) — the hidden window
|
|
|
|
|
|
|
|
|
|
|
|
Looks like a stock problem. It is a window problem.
|
|
|
|
|
|
Left = cheapest buy so far. Right = today. Carry one number: the min price.
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
def max_profit(prices: list[int]) -> int:
|
|
|
|
|
|
min_price = prices[0]
|
|
|
|
|
|
best = 0
|
|
|
|
|
|
|
|
|
|
|
|
for p in prices[1:]:
|
|
|
|
|
|
best = max(best, p - min_price)
|
|
|
|
|
|
min_price = min(min_price, p)
|
|
|
|
|
|
return best
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Character Replacement (LC 424) — window with a budget
|
|
|
|
|
|
|
|
|
|
|
|
Rule: window is valid if `window size − count of top letter ≤ k`.
|
|
|
|
|
|
That many replacements fix the window. Carry a frequency map.
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
from collections import defaultdict
|
|
|
|
|
|
|
|
|
|
|
|
def character_replacement(s: str, k: int) -> int:
|
|
|
|
|
|
count = defaultdict(int)
|
|
|
|
|
|
left = 0
|
|
|
|
|
|
best = 0
|
|
|
|
|
|
top = 0 # highest letter count seen
|
|
|
|
|
|
|
|
|
|
|
|
for right, c in enumerate(s):
|
|
|
|
|
|
count[c] += 1
|
|
|
|
|
|
top = max(top, count[c])
|
|
|
|
|
|
if (right - left + 1) - top > k: # over budget
|
|
|
|
|
|
count[s[left]] -= 1 # contract exactly one step
|
|
|
|
|
|
left += 1
|
|
|
|
|
|
best = max(best, right - left + 1)
|
|
|
|
|
|
return best
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## When the window fails
|
|
|
|
|
|
|
|
|
|
|
|
Sliding window needs a one-way rule: **growing can only hurt, shrinking can only help.**
|
|
|
|
|
|
Negative numbers break this — a bigger window can flip from bad to good.
|
|
|
|
|
|
That is when you reach for prefix sum + hashmap (LC 560) instead.
|
|
|
|
|
|
Say this decision out loud in the interview.
|
|
|
|
|
|
|
|
|
|
|
|
## Complexity
|
|
|
|
|
|
|
|
|
|
|
|
| | Time | Space |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| All patterns above | O(n) — each index enters and leaves once | O(1) or O(alphabet) |
|
|
|
|
|
|
|
|
|
|
|
|
## Close-out ritual
|
|
|
|
|
|
|
|
|
|
|
|
Before you submit, say out loud:
|
|
|
|
|
|
1. Time and space complexity — note each element enters and leaves the window at most once.
|
|
|
|
|
|
2. One edge case trace: empty string, all same char, or k larger than the string.
|
|
|
|
|
|
|
|
|
|
|
|
## Plan problems
|
|
|
|
|
|
|
|
|
|
|
|
**A-set:** LC 121 · 3 · 424 · 567 · 76
|
|
|
|
|
|
**B-set:** LC 209 · 1004 · 643
|
2026-08-19 13:05:47 -04:00
|
|
|
|
|
2026-08-20 12:00:02 -04:00
|
|
|
|
<YouTube id="QGNAVBn1_bc" title="Sliding Window" />
|
|
|
|
|
|
|