195 lines
6.2 KiB
YAML
195 lines
6.2 KiB
YAML
name: Sliding Window
|
|
slug: sliding-window
|
|
difficulty_level: 2
|
|
|
|
description: >
|
|
Maintain a window of elements that slides through the data, tracking a
|
|
constraint or computing aggregates. This transforms O(n*k) brute force into
|
|
O(n) by incrementally updating the window instead of recalculating from scratch.
|
|
|
|
when_to_use: |
|
|
- Finding subarrays/substrings with specific properties
|
|
- Maximum/minimum sum of fixed-size windows
|
|
- Longest substring with at most K distinct characters
|
|
- Problems mentioning "contiguous" elements
|
|
|
|
metaphor: |
|
|
Imagine looking at a landscape through a train window. As the train moves
|
|
forward, the scenery at the back of your view disappears while new scenery
|
|
appears at the front. You don't need to memorize the entire journey—just
|
|
keep track of what's currently visible through your window.
|
|
|
|
Another analogy: a cashier's sliding tray at a bank. As new items are added
|
|
to one end, old items fall off the other. You only count what's on the tray
|
|
at any moment.
|
|
|
|
core_concept: |
|
|
The **sliding window** technique avoids redundant computation by maintaining
|
|
state as the window moves. Instead of recalculating the entire window each
|
|
time, you *add* what enters and *remove* what leaves.
|
|
|
|
There are two main types:
|
|
|
|
1. **Fixed-size window**: Window size is constant (e.g., "find max sum of k elements")
|
|
2. **Variable-size window**: Window expands and contracts based on constraints
|
|
(e.g., "longest substring with at most 2 distinct characters")
|
|
|
|
The key insight is that consecutive windows share most of their elements.
|
|
Only the edges change, so only update those.
|
|
|
|
visualization: |
|
|
**Example: Maximum sum of 3 consecutive elements**
|
|
|
|
```
|
|
Array: [2, 1, 5, 1, 3, 2] Window size: 3
|
|
|
|
Window 1: [2, 1, 5] = 8 (calculate full sum)
|
|
└─────┘
|
|
|
|
Window 2: [1, 5, 1] = 8-2+1 = 7 (remove 2, add 1)
|
|
└─────┘
|
|
|
|
Window 3: [5, 1, 3] = 7-1+3 = 9 (remove 1, add 3) ← Maximum!
|
|
└─────┘
|
|
|
|
Window 4: [1, 3, 2] = 9-5+2 = 6 (remove 5, add 2)
|
|
└─────┘
|
|
```
|
|
|
|
**Variable window: Longest substring with at most 2 distinct chars**
|
|
|
|
```
|
|
String: "eceba"
|
|
|
|
"e" → 1 distinct, expand → length 1
|
|
"ec" → 2 distinct, expand → length 2
|
|
"ece" → 2 distinct, expand → length 3 ← Answer!
|
|
"eceb" → 3 distinct, shrink from left
|
|
"ceb" → 3 distinct, shrink from left
|
|
"eb" → 2 distinct, expand → length 2
|
|
"eba" → 3 distinct, shrink...
|
|
```
|
|
|
|
code_template: |
|
|
def fixed_window(arr: list, k: int) -> int:
|
|
"""Fixed-size sliding window."""
|
|
n = len(arr)
|
|
if n < k:
|
|
return 0
|
|
|
|
# Calculate initial window
|
|
window_sum = sum(arr[:k])
|
|
max_sum = window_sum
|
|
|
|
# Slide the window
|
|
for i in range(k, n):
|
|
window_sum += arr[i] - arr[i - k] # Add new, remove old
|
|
max_sum = max(max_sum, window_sum)
|
|
|
|
return max_sum
|
|
|
|
|
|
def variable_window(s: str, k: int) -> int:
|
|
"""Variable-size sliding window."""
|
|
char_count = {}
|
|
left = 0
|
|
max_length = 0
|
|
|
|
for right in range(len(s)):
|
|
# Expand: add character at right
|
|
char_count[s[right]] = char_count.get(s[right], 0) + 1
|
|
|
|
# Contract: shrink from left if constraint violated
|
|
while len(char_count) > k:
|
|
char_count[s[left]] -= 1
|
|
if char_count[s[left]] == 0:
|
|
del char_count[s[left]]
|
|
left += 1
|
|
|
|
# Update answer
|
|
max_length = max(max_length, right - left + 1)
|
|
|
|
return max_length
|
|
|
|
recognition_signals:
|
|
- "contiguous subarray"
|
|
- "substring"
|
|
- "maximum sum of k elements"
|
|
- "window"
|
|
- "consecutive"
|
|
- "at most k distinct"
|
|
- "minimum window"
|
|
- "longest substring"
|
|
- "sliding"
|
|
|
|
common_mistakes:
|
|
- title: Forgetting to handle window smaller than required
|
|
description: |
|
|
When array length is less than window size k, trying to create a window
|
|
causes index errors or incorrect results.
|
|
fix: |
|
|
Add an early check:
|
|
```python
|
|
if len(arr) < k:
|
|
return 0 # or appropriate default
|
|
```
|
|
|
|
- title: Off-by-one in variable window
|
|
description: |
|
|
When calculating window length, using `right - left` instead of
|
|
`right - left + 1` gives length off by one.
|
|
fix: |
|
|
Window length is always `right - left + 1` (inclusive on both ends).
|
|
|
|
- title: Not cleaning up empty entries in hash map
|
|
description: |
|
|
When shrinking a variable window, decrementing a counter to 0 but not
|
|
removing the key causes the distinct count to be wrong.
|
|
fix: |
|
|
Always delete keys when count reaches 0:
|
|
```python
|
|
if char_count[s[left]] == 0:
|
|
del char_count[s[left]]
|
|
```
|
|
|
|
- title: Updating answer at wrong time
|
|
description: |
|
|
For "minimum" problems, updating the answer inside the while loop
|
|
captures invalid states. For "maximum" problems, updating only inside
|
|
the while loop misses valid states.
|
|
fix: |
|
|
For maximum problems, update after expanding. For minimum problems,
|
|
update when the constraint is first satisfied (inside the while loop).
|
|
|
|
variations:
|
|
- name: Fixed-size window
|
|
description: |
|
|
Window size stays constant throughout. Simple slide operation: add one
|
|
element, remove one element.
|
|
example: "Maximum Sum Subarray of Size K, Find All Anagrams"
|
|
|
|
- name: Variable-size (shrinkable)
|
|
description: |
|
|
Window expands freely but contracts when constraints are violated.
|
|
Uses a while loop to shrink until valid.
|
|
example: "Longest Substring Without Repeating, Minimum Window Substring"
|
|
|
|
- name: Two-pointer variant
|
|
description: |
|
|
Some problems use two pointers that feel like sliding window but track
|
|
different metrics. The mechanics are similar.
|
|
example: "Container With Most Water, Trapping Rain Water"
|
|
|
|
- name: Caterpillar method
|
|
description: |
|
|
Another name for the variable sliding window, emphasizing how the window
|
|
stretches and contracts like a caterpillar moving.
|
|
example: "Common in competitive programming contexts"
|
|
|
|
related_patterns:
|
|
- two-pointers
|
|
- prefix-sum
|
|
|
|
prerequisite_patterns:
|
|
- two-pointers
|