Minimum Window Substring
Hard · Sliding Window · Explanation and code example
Sliding Window
How it works
We want the smallest window in s that contains all characters of t (with the right counts).
Instead of checking all substrings, we use a sliding window:
- Expand the window by moving the right pointer
rand adding characters into awindowmap. - Once the window has all required characters (i.e., it "covers"
t), we try to shrink it from the left with pointerlto make it as small as possible while still valid.
During this process, we keep track of the best (smallest) window seen so far. This way, we only scan each character at most two times, making it efficient and still easy to follow.
Steps
- If
tis empty, return"". - Build a frequency map
countTfor characters int. - Initialize:
windowas an empty map for the current window counts.have = 0= how many characters currently meet the required count.need = len(countT)= how many distinct characters we need to match.res = [-1, -1]andresLen = infinityto store the best window.
- Use a right pointer
rto expand the window overs:- Add
s[r]towindow. - If
s[r]is incountTand its count inwindowmatchescountT, incrementhave.
- Add
- When
have == need, the window is valid:- Update the best result if the current window is smaller.
- Then shrink from the left:
- Decrease the count of
s[l]inwindow. - If
s[l]is incountTand its count inwindowfalls belowcountT, decrementhave. - Move
lright.
- Decrease the count of
- After the loop, return the substring defined by
resif found; otherwise, return"".
Complexity
- Time complexity:
O(n + m) - Space complexity:
O(k)
Where
nis the length of the strings,mis the length of the stringt, andkis the total number of unique characters insandt.Building the frequency map takes
O(m)time. During the sliding-window pass, the right pointer traversessonce and the left pointer advances at mostntimes in total, so this pass takesO(2n) = O(n)time.
Things to watch for
Not Handling Duplicate Characters in Target
The target string t may contain duplicate characters (e.g., "AAB"). Simply checking for character presence is insufficient; you must track the exact count of each character and ensure the window contains at least that many occurrences.
Shrinking the Window Too Aggressively
When contracting the window from the left, some implementations remove characters before checking if the window is still valid. Always update the result before shrinking, and only shrink while the window remains valid.
Incorrect Validity Check Logic
Using have == need requires careful management: have should only increment when a character's count exactly reaches the required amount, and only decrement when it falls below. Incrementing have every time a required character is added leads to overcounting.
Forgetting to Handle Empty Target String
When t is empty, the minimum window is an empty string. Failing to handle this edge case at the start can lead to unexpected behavior or incorrect results.
Off-by-One Errors in Substring Extraction
When storing and returning the result window, confusing inclusive vs. exclusive bounds leads to returning a substring that is one character too short or too long. Ensure consistency between how you store indices and how you extract the final substring.
Python code example
class Solution:
def minWindow(self, s: str, t: str) -> str:
if t == "":
return ""
countT, window = {}, {}
for c in t:
countT[c] = 1 + countT.get(c, 0)
have, need = 0, len(countT)
res, resLen = [-1, -1], float("infinity")
l = 0
for r in range(len(s)):
c = s[r]
window[c] = 1 + window.get(c, 0)
if c in countT and window[c] == countT[c]:
have += 1
while have == need:
if (r - l + 1) < resLen:
res = [l, r]
resLen = r - l + 1
window[s[l]] -= 1
if s[l] in countT and window[s[l]] < countT[s[l]]:
have -= 1
l += 1
l, r = res
return s[l : r + 1] if resLen != float("infinity") else ""
C++ code example
class Solution {
public:
string minWindow(string s, string t) {
if (t.empty()) return "";
unordered_map<char, int> countT, window;
for (char c : t) {
countT[c]++;
}
int have = 0, need = countT.size();
pair<int, int> res = {-1, -1};
int resLen = INT_MAX;
int l = 0;
for (int r = 0; r < s.length(); r++) {
char c = s[r];
window[c]++;
if (countT.count(c) && window[c] == countT[c]) {
have++;
}
while (have == need) {
if ((r - l + 1) < resLen) {
resLen = r - l + 1;
res = {l, r};
}
window[s[l]]--;
if (countT.count(s[l]) && window[s[l]] < countT[s[l]]) {
have--;
}
l++;
}
}
return resLen == INT_MAX ? "" : s.substr(res.first, resLen);
}
};
Source and license
Adapted from NeetCode’s source · MIT license. Node types are supplied by LeetCode where needed.