mirror of
https://github.com/prdlk/leetcode.git
synced 2026-09-16 23:16:26 +00:00
118 lines
3.4 KiB
Plaintext
118 lines
3.4 KiB
Plaintext
---
|
||
title: 'Sliding Window'
|
||
description: 'Two pointers plus incremental state between them.'
|
||
---
|
||
|
||
## The idea
|
||
|
||
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
|
||
|
||
<YouTube id="QGNAVBn1_bc" title="Sliding Window" />
|
||
|