Minimum Window Substring
Smallest substring of s covering every character of t — variable-size sliding window with a 'missing' counter.
Problem#
Given strings s and t, return the smallest window in s that contains every character of t (with multiplicity). Return "" if no such window exists.
Examples#
s = "ADOBECODEBANC", t = "ABC"→"BANC"s = "a", t = "a"→"a"s = "a", t = "aa"→""
Constraints#
1 <= s.length, t.length <= 10^5.
Hints#
Hint 1
missing counter equal to t.length initially; decrement only when a needed character is actually still needed. Approach#
Track need[c] = count of each character we still owe (negative = surplus). missing is the sum of positive need. Expand right; whenever the window covers t, contract left greedily and record the best window.
Solution#
class Solution {public: string minWindow(string s, string t) { if (t.size() > s.size()) return ""; int need[256] = {0}; for (char c : t) ++need[(unsigned char)c]; int missing = t.size(); int bestL = 0, bestLen = INT_MAX; int left = 0; for (int right = 0; right < (int)s.size(); ++right) { if (need[(unsigned char)s[right]]-- > 0) --missing; while (missing == 0) { if (right - left + 1 < bestLen) { bestLen = right - left + 1; bestL = left; } if (++need[(unsigned char)s[left++]] > 0) ++missing; } } return bestLen == INT_MAX ? "" : s.substr(bestL, bestLen); }};func minWindow(s string, t string) string { if len(t) > len(s) { return "" } need := [256]int{} for i := 0; i < len(t); i++ { need[t[i]]++ } missing := len(t) bestL, bestLen := 0, math.MaxInt32 left := 0 for right := 0; right < len(s); right++ { if need[s[right]] > 0 { missing-- } need[s[right]]-- for missing == 0 { if right-left+1 < bestLen { bestLen = right - left + 1 bestL = left } need[s[left]]++ if need[s[left]] > 0 { missing++ } left++ } } if bestLen == math.MaxInt32 { return "" } return s[bestL : bestL+bestLen]}from collections import Counter
class Solution: def minWindow(self, s: str, t: str) -> str: if len(t) > len(s): return "" need = Counter(t) missing = len(t) best_l, best_len = 0, float('inf') left = 0 for right, ch in enumerate(s): if need[ch] > 0: missing -= 1 need[ch] -= 1 while missing == 0: if right - left + 1 < best_len: best_len = right - left + 1 best_l = left need[s[left]] += 1 if need[s[left]] > 0: missing += 1 left += 1 if best_len == float('inf'): return "" return s[best_l:best_l + best_len]function minWindow(s, t) { if (t.length > s.length) return ""; const need = new Array(256).fill(0); for (let i = 0; i < t.length; i++) need[t.charCodeAt(i)]++; let missing = t.length; let bestL = 0, bestLen = Infinity; let left = 0; for (let right = 0; right < s.length; right++) { const rc = s.charCodeAt(right); if (need[rc] > 0) missing--; need[rc]--; while (missing === 0) { if (right - left + 1 < bestLen) { bestLen = right - left + 1; bestL = left; } const lc = s.charCodeAt(left); need[lc]++; if (need[lc] > 0) missing++; left++; } } return bestLen === Infinity ? "" : s.substring(bestL, bestL + bestLen);}class Solution { public String minWindow(String s, String t) { if (t.length() > s.length()) return ""; int[] need = new int[256]; for (int i = 0; i < t.length(); i++) need[t.charAt(i)]++; int missing = t.length(); int bestL = 0, bestLen = Integer.MAX_VALUE; int left = 0; for (int right = 0; right < s.length(); right++) { char rc = s.charAt(right); if (need[rc] > 0) missing--; need[rc]--; while (missing == 0) { if (right - left + 1 < bestLen) { bestLen = right - left + 1; bestL = left; } char lc = s.charAt(left); need[lc]++; if (need[lc] > 0) missing++; left++; } } return bestLen == Integer.MAX_VALUE ? "" : s.substring(bestL, bestL + bestLen); }}function minWindow(s: string, t: string): string { if (t.length > s.length) return ""; const need: number[] = new Array(256).fill(0); for (let i = 0; i < t.length; i++) need[t.charCodeAt(i)]++; let missing = t.length; let bestL = 0, bestLen = Infinity; let left = 0; for (let right = 0; right < s.length; right++) { const rc = s.charCodeAt(right); if (need[rc] > 0) missing--; need[rc]--; while (missing === 0) { if (right - left + 1 < bestLen) { bestLen = right - left + 1; bestL = left; } const lc = s.charCodeAt(left); need[lc]++; if (need[lc] > 0) missing++; left++; } } return bestLen === Infinity ? "" : s.substring(bestL, bestL + bestLen);}Editorial#
The dual-tracker pattern is the elegance: need may go negative (we have surplus of that character), but missing only tracks the deficit. Decrementing missing on add and incrementing on remove is gated by need crossing zero — that’s what allows surplus characters to be discarded freely while never under-counting needed ones. Each pointer advances at most n times → O(n + m).
Complexity#
- Time: O(n + m).
- Space: O(|Σ|).
Concept revision#
Related#