Some window problems want the shortest stretch that meets a requirement: a sum of at least target, or a substring containing every character of t. The loop looks like the longest valid window, with one flip. You shrink while the window is valid and record the answer each time, because every shrink that stays valid is a better answer.
The idea#
The two window types differ in when they shrink and when they record.
| Longest valid window | Minimum window | |
|---|---|---|
| Shrink while | the window is invalid | the window is valid |
| Record the answer | after the while loop | inside the while loop, before each shrink |
| Goal of shrinking | get back to valid | find the smallest valid window |
This works because the requirement, once met, stays met as the window grows. So for each end, shrinking from the left until the window breaks finds the shortest valid window that ends there.
That leaves the validity check itself. When the requirement is a set of character counts ("contains all of t, with multiplicity"), comparing two whole counters every step is slow. Use have/need instead:
need= the number of distinct characters int.have= how many of those characters currently meet their required count in the window.havegoes up by one at the exact moment a character's window count reaches its required count, and down by one at the moment it drops below.
The window is valid exactly when have == need, an check.
How to spot it#
The question asks for the smallest stretch. Look for these signals.
- "Minimum length / smallest substring or subarray such that ...".
- "Contains all characters of
t", "is a permutation / anagram ofp". - A requirement that, once met, stays met as the window grows (so shrinking from the left until it breaks finds the shortest).
Template#
The template uses the have/need form, since it covers the harder case. For a plain sum, replace the have/need check with the sum condition.
need_count = required count of each character
need = number of distinct required characters
have = 0, start = 0, best = none
for end in range(len(s)):
add s[end] to window counts
if its count just reached its quota: have += 1
while have == need: # valid: record, then shrink
best = shorter of best and window start..end
remove s[start] from window counts
if its count just fell below its quota: have -= 1
start += 1
return best, or "no window" if best is none
Each pointer moves at most n times, so the scan is .
Exact match: permutation or anagram#
Permutation and anagram problems are a stricter version of the same check. When the window must match p's counts exactly, its size is fixed at len(p). Two ways keep it exact:
- Shrink on overflow: after adding
s[end],whileits count is more thanpneeds, remove from the left. Thenhave == needmeans the window is an exact match. - Fixed size: keep the window at
len(p)like a fixed-size window and checkhave == needeach step.
Pitfalls#
Most bugs here come from have drifting out of step with the window.
- Updating
havein the wrong order when shrinking. When you removes[start], check whether its count currently equals the quota before you decrement, or compare with<after it. Forgettinghave -= 1on removal entirely leaveshavecounting characters that left the window. - Counting
haveon every increment.havechanges only when a count crosses the quota exactly. Using>=would add one per extra copy. needis the number of distinct characters,len(Counter(t)), notlen(t).- Recording after the loop. In a minimum window the valid windows exist inside the while loop. After it exits, the window is invalid.
- No valid window at all. Use a sentinel length and return
""or0when it never changes.
Problems in this pattern#
These problems use this pattern. Solve them before you open the hints below. Start with the first one.
- 209. Minimum Size Subarray Sum
- 567. Permutation in String
- 438. Find All Anagrams in a String
- 76. Minimum Window Substring
Hints#
Open a hint only after you have tried the problem for a while.
Hint: Minimum Size Subarray Sum
The state is a running sum. Shrink while window_sum >= target: record the length, subtract nums[start], move start. Return 0 if no window ever qualified.
Hint: Minimum Window Substring
This is the full have/need template. Keep the best (length, start) pair and slice once at the end. If the best length never changed from len(s) + 1, return "".
Hint: Permutation in String
The window must match p exactly, so its size is len(p). Use the fixed-size form or shrink on overflow, and return as soon as have == need. Don't forget have -= 1 when a removed character drops below its quota.
Hint: Find All Anagrams in a String
This is Permutation in String, but you collect every match. Record start when have == need, then move start forward by one, updating have and the counter for the character you drop.
Key takeaways#
- Minimum window: shrink while valid and record inside the loop.
havecounts characters whose quota is met; the window is valid whenhave == need.havechanges only when a count crosses its quota, in either direction.- Anagram and permutation checks are have/need on a window of exactly
len(p).