|
| 1 | +# Sliding Window: Pattern Intuition Guide |
| 2 | + |
| 3 | +> *"The window is a moving lens of attention — it forgets the past to focus on what matters now."* |
| 4 | +
|
| 5 | +--- |
| 6 | + |
| 7 | +## The Situation That Calls for a Window |
| 8 | + |
| 9 | +Imagine you're walking through a long corridor, and you can only see through a rectangular frame you carry with you. As you move forward, new things enter your view on the right, and old things disappear on the left. |
| 10 | + |
| 11 | +**This is the essence of Sliding Window.** |
| 12 | + |
| 13 | +You encounter this pattern whenever: |
| 14 | +- You're scanning through a sequence (string, array, stream) |
| 15 | +- You care about a **contiguous portion** of that sequence |
| 16 | +- The answer depends on properties of that portion |
| 17 | +- Those properties can be **updated incrementally** as the portion shifts |
| 18 | + |
| 19 | +The key insight: *You don't need to remember everything — only what's currently in view.* |
| 20 | + |
| 21 | +--- |
| 22 | + |
| 23 | +## The Two Forces at Play |
| 24 | + |
| 25 | +Every sliding window algorithm is a dance between two opposing forces: |
| 26 | + |
| 27 | +### The Explorer (Right Boundary) |
| 28 | +- Always moves forward, never backward |
| 29 | +- Discovers new territory |
| 30 | +- Adds new elements to consideration |
| 31 | +- Asks: *"What happens if I include this?"* |
| 32 | + |
| 33 | +### The Gatekeeper (Left Boundary) |
| 34 | +- Follows behind, cleaning up |
| 35 | +- Removes elements that no longer serve the goal |
| 36 | +- Enforces the rules of what's allowed |
| 37 | +- Asks: *"Must I let go of something to stay valid?"* |
| 38 | + |
| 39 | +The Explorer is eager and expansive. The Gatekeeper is disciplined and selective. Together, they maintain a **window of validity** that slides through the sequence. |
| 40 | + |
| 41 | +--- |
| 42 | + |
| 43 | +## The Invariant: The Window's Promise |
| 44 | + |
| 45 | +At every moment, the window makes a promise — an **invariant** that must always be true: |
| 46 | + |
| 47 | +| Problem Type | The Promise | |
| 48 | +|--------------|-------------| |
| 49 | +| Longest unique substring | *"Every character in my view appears exactly once"* | |
| 50 | +| At most K distinct | *"I contain no more than K different characters"* | |
| 51 | +| Minimum covering substring | *"I contain everything required"* | |
| 52 | +| Sum at least target | *"My total meets or exceeds the goal"* | |
| 53 | + |
| 54 | +**This promise is sacred.** The moment it's broken, the Gatekeeper must act — shrinking the window until the promise is restored. |
| 55 | + |
| 56 | +--- |
| 57 | + |
| 58 | +## The Irreversible Truth |
| 59 | + |
| 60 | +Here's what makes sliding window work: **the Explorer never retreats.** |
| 61 | + |
| 62 | +Once the right boundary passes an element, that element has been "seen." We may include it or exclude it from our current window, but we never go back to re-examine it as a potential starting point... unless the Gatekeeper releases it. |
| 63 | + |
| 64 | +This one-directional march is what gives us O(n) time complexity. Each element enters the window at most once and exits at most once. No element is visited more than twice across the entire algorithm. |
| 65 | + |
| 66 | +The irreversibility creates efficiency: *past decisions don't haunt us.* |
| 67 | + |
| 68 | +--- |
| 69 | + |
| 70 | +## The Two Modes of Seeking |
| 71 | + |
| 72 | +Depending on what you're optimizing, the dance changes: |
| 73 | + |
| 74 | +### Mode 1: Maximize the Window |
| 75 | +*"How large can my view become while staying valid?"* |
| 76 | + |
| 77 | +``` |
| 78 | +Process: |
| 79 | +1. Explorer advances, adding new element |
| 80 | +2. If promise breaks → Gatekeeper advances until promise restored |
| 81 | +3. Record the current window size (this is a candidate answer) |
| 82 | +4. Repeat |
| 83 | +
|
| 84 | +The window EXPANDS freely, CONTRACTS only when forced. |
| 85 | +``` |
| 86 | + |
| 87 | +**Mental image**: Stretching a rubber band until it's about to snap, then easing off just enough. |
| 88 | + |
| 89 | +### Mode 2: Minimize the Window |
| 90 | +*"How small can my view become while still being valid?"* |
| 91 | + |
| 92 | +``` |
| 93 | +Process: |
| 94 | +1. Explorer advances until promise becomes TRUE |
| 95 | +2. While promise holds → Gatekeeper advances, shrinking window |
| 96 | +3. Record the window size just before promise breaks |
| 97 | +4. Repeat |
| 98 | +
|
| 99 | +The window EXPANDS until valid, then CONTRACTS aggressively. |
| 100 | +``` |
| 101 | + |
| 102 | +**Mental image**: Tightening a noose around the minimal solution. |
| 103 | + |
| 104 | +--- |
| 105 | + |
| 106 | +## Pattern Recognition: "Is This a Sliding Window Problem?" |
| 107 | + |
| 108 | +Ask yourself these questions: |
| 109 | + |
| 110 | +``` |
| 111 | +┌─────────────────────────────────────────────────────────────┐ |
| 112 | +│ 1. Am I looking for a CONTIGUOUS subarray or substring? │ |
| 113 | +│ └── No? → Not sliding window │ |
| 114 | +│ │ |
| 115 | +│ 2. Can I describe a PROPERTY that makes a window valid? │ |
| 116 | +│ └── No? → Probably not sliding window │ |
| 117 | +│ │ |
| 118 | +│ 3. Can I UPDATE that property in O(1) when I add/remove │ |
| 119 | +│ a single element? │ |
| 120 | +│ └── No? → Sliding window won't give O(n) │ |
| 121 | +│ │ |
| 122 | +│ 4. Is the answer about OPTIMIZING that window │ |
| 123 | +│ (longest, shortest, exists)? │ |
| 124 | +│ └── Yes to all? → SLIDING WINDOW. │ |
| 125 | +└─────────────────────────────────────────────────────────────┘ |
| 126 | +``` |
| 127 | + |
| 128 | +--- |
| 129 | + |
| 130 | +## The Three Window Shapes |
| 131 | + |
| 132 | +### Shape 1: Variable Window, Maximize |
| 133 | +**Story**: *"I want the biggest room that still follows the rules."* |
| 134 | + |
| 135 | +- Invariant: Some constraint must not be violated |
| 136 | +- Strategy: Grow greedily, shrink reluctantly |
| 137 | +- Answer: Largest valid window seen |
| 138 | + |
| 139 | +**Classic problems**: Longest substring without repeating characters, longest with at most K distinct |
| 140 | + |
| 141 | +### Shape 2: Variable Window, Minimize |
| 142 | +**Story**: *"I want the smallest container that still holds everything I need."* |
| 143 | + |
| 144 | +- Invariant: Some requirement must be satisfied |
| 145 | +- Strategy: Grow until valid, shrink aggressively |
| 146 | +- Answer: Smallest valid window seen |
| 147 | + |
| 148 | +**Classic problems**: Minimum window substring, minimum size subarray sum |
| 149 | + |
| 150 | +### Shape 3: Fixed Window |
| 151 | +**Story**: *"I'm looking through a frame of exact size — does it ever show what I'm looking for?"* |
| 152 | + |
| 153 | +- Invariant: Window size exactly K |
| 154 | +- Strategy: Add one, remove one, check condition |
| 155 | +- Answer: Whether/where condition is met |
| 156 | + |
| 157 | +**Classic problems**: Find all anagrams, check permutation inclusion |
| 158 | + |
| 159 | +--- |
| 160 | + |
| 161 | +## The State: What the Window Remembers |
| 162 | + |
| 163 | +The window isn't just boundaries — it carries **state** about its contents: |
| 164 | + |
| 165 | +| What You're Tracking | State Structure | Update Cost | |
| 166 | +|---------------------|-----------------|-------------| |
| 167 | +| Character uniqueness | Last-seen index map | O(1) | |
| 168 | +| Character frequencies | Count map | O(1) | |
| 169 | +| Distinct count | Map + size | O(1) | |
| 170 | +| Running sum | Single integer | O(1) | |
| 171 | +| Requirement satisfaction | "Have" vs "Need" counters | O(1) | |
| 172 | + |
| 173 | +The magic of sliding window is that these states are **incrementally maintainable**. Adding an element updates the state. Removing an element reverses that update. No full recomputation needed. |
| 174 | + |
| 175 | +--- |
| 176 | + |
| 177 | +## Visualizing the Dance |
| 178 | + |
| 179 | +``` |
| 180 | +Sequence: [ a b c a b c b b ] |
| 181 | + ↑ |
| 182 | + Both start here |
| 183 | +
|
| 184 | +Step 1: [ a ] Explorer sees 'a' |
| 185 | + L R |
| 186 | +
|
| 187 | +Step 2: [ a b ] Explorer sees 'b' |
| 188 | + L R |
| 189 | +
|
| 190 | +Step 3: [ a b c ] Explorer sees 'c' |
| 191 | + L R |
| 192 | +
|
| 193 | +Step 4: [ a b c a ] Explorer sees 'a' — duplicate! |
| 194 | + L R |
| 195 | + |
| 196 | + Gatekeeper must act: Move L past the first 'a' |
| 197 | + |
| 198 | + [ b c a ] Promise restored |
| 199 | + L R |
| 200 | +
|
| 201 | +Step 5: [ b c a b ] Explorer sees 'b' — duplicate! |
| 202 | + L R |
| 203 | + |
| 204 | + Gatekeeper moves: |
| 205 | + |
| 206 | + [ c a b ] Promise restored |
| 207 | + L R |
| 208 | +``` |
| 209 | + |
| 210 | +Notice: The Explorer always advances. The Gatekeeper only moves when the promise breaks. Together, they visit each element at most twice. |
| 211 | + |
| 212 | +--- |
| 213 | + |
| 214 | +## The Moment of Recognition |
| 215 | + |
| 216 | +You're reading a problem. You see phrases like: |
| 217 | +- *"contiguous subarray"* |
| 218 | +- *"substring"* |
| 219 | +- *"longest/shortest"* |
| 220 | +- *"at most K"* |
| 221 | +- *"containing all of"* |
| 222 | + |
| 223 | +And you feel it: *This is about maintaining something over a moving portion.* |
| 224 | + |
| 225 | +That's your cue. The Explorer and Gatekeeper are ready. The window wants to slide. |
| 226 | + |
| 227 | +--- |
| 228 | + |
| 229 | +## From Intuition to Implementation |
| 230 | + |
| 231 | +Only now — after the dance is clear — does code become useful. |
| 232 | + |
| 233 | +The template is always the same skeleton: |
| 234 | + |
| 235 | +```python |
| 236 | +def sliding_window(sequence): |
| 237 | + state = initial_state() |
| 238 | + left = 0 |
| 239 | + answer = initial_answer() |
| 240 | + |
| 241 | + for right, element in enumerate(sequence): |
| 242 | + # Explorer: include new element |
| 243 | + update_state_add(state, element) |
| 244 | + |
| 245 | + # Gatekeeper: enforce the promise |
| 246 | + while promise_is_broken(state): |
| 247 | + update_state_remove(state, sequence[left]) |
| 248 | + left += 1 |
| 249 | + |
| 250 | + # Record: this window is valid |
| 251 | + answer = consider(answer, left, right) |
| 252 | + |
| 253 | + return answer |
| 254 | +``` |
| 255 | + |
| 256 | +The variations come from: |
| 257 | +1. **What is the promise?** (determines the while condition) |
| 258 | +2. **What state do we track?** (determines the data structure) |
| 259 | +3. **What are we optimizing?** (determines how we update the answer) |
| 260 | + |
| 261 | +--- |
| 262 | + |
| 263 | +## Quick Reference: Problem → Pattern Mapping |
| 264 | + |
| 265 | +| When You See... | Think... | Window Type | |
| 266 | +|----------------|----------|-------------| |
| 267 | +| "Longest substring with unique chars" | Uniqueness promise | Maximize | |
| 268 | +| "Longest with at most K distinct" | Count limit promise | Maximize | |
| 269 | +| "Minimum window containing all of T" | Coverage promise | Minimize | |
| 270 | +| "Subarray sum ≥ target" | Threshold promise | Minimize | |
| 271 | +| "Contains permutation" | Exact match promise | Fixed | |
| 272 | +| "Find all anagrams" | Exact match, collect all | Fixed | |
| 273 | + |
| 274 | +--- |
| 275 | + |
| 276 | +## The Pattern in One Sentence |
| 277 | + |
| 278 | +> *Sliding Window is the art of maintaining a valid contiguous view by advancing eagerly and retreating only when necessary.* |
| 279 | +
|
| 280 | +When you see a problem about optimizing over contiguous sequences with incrementally checkable properties — you've found your window. |
| 281 | + |
| 282 | +Let it slide. |
| 283 | + |
| 284 | +--- |
| 285 | + |
| 286 | +*For detailed implementations and code examples, see [templates.md](./templates.md).* |
| 287 | + |
0 commit comments