Sliding Window Technique
Maintain a running window over a contiguous subarray or substring โ expand it to include more, shrink it to restore a constraint. Every element enters and exits the window at most once, giving O(n) where brute force needs O(nยฒ).
What is Sliding Window?
Sliding window maintains a contiguous subarray (or substring) bounded by two pointers โ left and right. The right pointer expands the window to include more elements; the left pointer contracts it to restore a violated constraint. The crucial insight: instead of recomputing the window's aggregate (sum, count, frequency map) from scratch each time, you incrementally update it โ add the new element entering on the right, remove the old element exiting on the left. Each element enters the window once and exits once โ O(n) total, regardless of window size. The technique comes in two flavours: fixed-size windows (maximum sum of subarray with exactly k elements) and variable-size windows (smallest subarray whose sum โฅ target, longest substring without repeating characters). Fixed windows always have size k; variable windows expand until a condition is met, then shrink until it's violated, tracking the best result at each valid state.
How It Thinks
Pattern 1 โ Fixed-Size Window
The window is always exactly k elements wide. Build the initial window of size k, then slide one position at a time โ add the new element on the right, remove the old element on the left. Update the answer at each step. This is the simplest sliding window pattern.
- The window always has exactly k elements. Just slide and track the best.
- The only variation is what you track: maximum sum, maximum average, count of distinct elements, etc.
Pattern 2 โ Variable-Size Window
The window size varies โ expand the right end to include more elements until a constraint is met (or violated), then shrink the left end to restore (or break) the constraint. Track the best valid window seen so far. This pattern handles "minimum length subarray with sum โฅ S" and "longest substring with at most K distinct characters."
while loop makes it O(nยฒ). It doesn't โ the left pointer only moves forward, and it moves at most n times total across ALL iterations of the outer loop. Total work: right moves n times + left moves at most n times = O(2n) = O(n). This is the amortised analysis that makes sliding window O(n).
Sliding Window on Strings
String sliding window problems use a frequency map as the window state โ typically int[128] for ASCII characters or a HashMap<Character, Integer>. The key mechanics: when a character enters the window (right advances), increment its count. When it exits (left advances), decrement its count. The "constraint" is usually about character uniqueness or required character frequencies.
- When you encounter a repeated character, jump left directly to
lastSeen[c] + 1instead of shrinking one step at a time. - Same O(n) complexity but fewer iterations in practice:
left = max(left, map.get(c) + 1).
Advanced โ Window With Frequency Counts
The hardest sliding window problems โ like "Minimum Window Substring" (LC 76) โ require tracking whether the window satisfies a complex condition involving multiple characters. The pattern: maintain a frequency map of required characters, a frequency map of window characters, and a counter tracking how many required characters have their full count satisfied. When the counter equals the number of distinct required characters, the window is valid.
- Without it, you'd check all 26+ characters every time to see if the window is valid โ O(26) per step.
- With the "have" counter that increments/decrements when a character reaches/drops below its required count, validity checking is O(1).
- This is what makes LC 76 tractable.
Recognising Sliding Window Problems
| Signal in Problem | Window Type | Examples |
|---|---|---|
| "maximum/minimum subarray of size k" | Fixed | Max sum subarray, max average |
| "shortest/smallest subarray with sum โฅ X" | Variable (shrink when valid) | Min size subarray sum (LC 209) |
| "longest substring with at most K distinct" | Variable (shrink when invalid) | Longest with K distinct (LC 340) |
| "longest substring without repeating" | Variable (shrink when invalid) | Longest without repeat (LC 3) |
| "smallest window containing all of t" | Variable (shrink when valid) | Min window substring (LC 76) |
| "count of subarrays with exactly K" | Variable (atMost(K) - atMost(K-1)) | Subarrays with K distinct (LC 992) |
Finding MINIMUM window
Expand until condition is met โ โ record/update answer โ shrink to find a shorter valid window โ keep shrinking while still valid
- Min size subarray sum โฅ target
- Min window substring
Finding MAXIMUM window
Expand until condition is violated โ โ shrink until condition is restored โ โ record/update answer โ expand again
- Longest substring without repeating
- Longest with at most K distinct
- Sliding window IS a specialised form of two pointers โ both use left and right indices.
- The distinction: sliding window maintains a running "window state" (sum, frequency map, count) that is incrementally updated.
- Two pointers (opposite ends) does not maintain state between the two pointers โ it makes a decision based on the values at L and R directly.
Build It Once
Build the fixed and variable templates once. The code is short โ the challenge is recognising which template applies and what "window state" to maintain.
Use It In Java
int[] freq = new int[128]; โ fastest for ASCII chars, O(1) lookup Map<Character, Integer> map = new HashMap<>(); โ use merge(c, 1, Integer::sum) s.charAt(i) โ returns char, usable as array index for int[128] | Idiom | Use case | Note |
|---|---|---|
freq[s.charAt(r)]++ | Character enters window | O(1) โ direct array indexing with char |
freq[s.charAt(left++)]-- | Character exits window | Decrement and advance left in one line |
map.merge(c, 1, Integer::sum) | Increment count in HashMap | Cleaner than getOrDefault + put |
map.merge(c, -1, Integer::sum) | Decrement count | Follow with if (map.get(c)==0) map.remove(c) |
Math.max(max, r - left + 1) | Update longest window | Window length = right - left + 1 |
Integer.MAX_VALUE | Initial value for "minimum" problems | Check at end: min == MAX_VALUE ? 0 : min |
map.size() for "number of distinct characters," you must remove keys when their count drops to 0 โ otherwise size() includes characters that are no longer in the window. Use if (map.get(c) == 0) map.remove(c) after decrementing.
int[128] is faster than HashMap for ASCII-only problems โ no boxing, no hashing, no collision handling. Use it for all lowercase/uppercase/ASCII sliding window string problems. Reserve HashMap for Unicode or when you need .size() for distinct-count tracking.
Problems To Solve
Sliding window problems all share one signal: "contiguous subarray/substring" + an aggregate condition (sum, count, uniqueness). Fixed-size problems are easy warm-ups. Variable-size problems are the real interview challenges โ the hardest part is deciding WHEN to shrink: shrink when valid (minimum problems) or shrink when invalid (maximum problems). Once you internalise this distinction, every sliding window problem is the same template with a different window state and shrink condition.
| Difficulty | Pattern | Problem | Key Insight |
|---|---|---|---|
| Easy | fixed | Maximum Average Subarray I โ LC 643 | Fixed window of size k. Track running sum, compute average at each position. Pure fixed template. |
| Medium | variable | Longest Substring Without Repeating โ LC 3 | Variable window + frequency array. Shrink when a character's count exceeds 1 (invalid). Track max window length. |
| Medium | variable | Minimum Size Subarray Sum โ LC 209 | Variable window. Shrink when sum โฅ target (valid) โ record length, keep shrinking. Track min window length. |
| Medium | variable | Max Consecutive Ones III โ LC 1004 | Variable window allowing at most k zeroes. Window state: count of zeroes. Shrink when zeroes > k. Track max window length. |
| Medium | variable | Permutation in String โ LC 567 | Fixed-ish window of size len(s1) on s2. Compare frequency arrays at each position. Alternatively, use a "matches" counter. |
| Hard | hashmap | Minimum Window Substring โ LC 76 | Variable window + need/have counters. Shrink when all required characters are satisfied (valid). Track minimum valid window. |
- Read the problem. See "contiguous subarray" or "substring"? โ Sliding window.
- See "sum โฅ target" or "at most K distinct"? โ Variable window. See "of size K"? โ Fixed window.
- Then: (1) decide what state the window tracks (sum, frequency map, count), (2) decide when to shrink (valid โ minimum, invalid โ maximum), (3) write the template.
- Done.