What is the Minimum Window Substring Problem?
Minimum Window Substring is one of those problems that shows up in almost every coding interview. At first glance it looks a bit tricky, but once you understand the sliding window technique behind it, the whole thing clicks into place.
The basic idea is simple: you're given two strings — a big string s and a small target string t. Your job is to find the shortest part of s that contains every character of t, including duplicates.
You'll see this problem a lot in technical interviews because it tests your ability to think about substrings, character frequency, and efficient window management — all at once. It's also a classic example of how the sliding window technique can replace a slow brute force approach and make your code run much faster.
Let's go through it step by step — from understanding the problem to writing a clean Python program and testing it with multiple test cases.
Understanding the Problem
Given two strings s and t, return the minimum window substring of s such that every character in t (including duplicates) is present in the window. If no such window exists, return an empty string "".
Let's understand this with a quick example:
Say s = "ADOBECODEBANC" and t = "ABC". We need to find the smallest part of s that includes A, B, and C. The answer is "BANC", which starts at index 9.
Notice that "ADOBEC" also contains A, B, and C — but "BANC" is shorter, so that's our answer.
🔑 Key insight: We don't care about the order of characters inside the window. We only care that every character from t appears at least as many times as it does in t.
Input Format
s = "ADOBECODEBANC"
t = "ABC"
1 ≤ s.length ≤ 10⁵
1 ≤ t.length ≤ 10⁴
s, t consist of English letters
s— the main string we search inside (can be uppercase or lowercase).t— the target string whose characters we need to include in our window.- Characters in
tmay repeat — and we must match all occurrences.
Output Format
"BANC"
""
- Return the smallest substring of
sthat contains all characters oft. - If multiple windows have the same minimum length, return the one that appears first.
- If no valid window exists, return an empty string
"".
Algorithm Flowchart
Here's a simple visual of how the sliding window moves through the string to find the minimum window substring:
The window expands by moving right and shrinks by moving left — every time we have all characters matched, we try to minimize.
Step-by-Step Pseudocode
Before diving into real Python, here's the logic in plain pseudocode so you can see the steps clearly:
Python Solution (Clean & Commented)
Here's the full Python program using the sliding window technique. Every important line is commented so you know exactly what's happening:
from collections import Counter
def min_window_substring(s: str, t: str) -> str:
# Edge case: if either string is empty, return ""
if not s or not t:
return ""
# Frequency map for characters in t
t_map = Counter(t)
# 'need' = how many unique chars of t we must satisfy
need = len(t_map)
# 'have' = how many unique chars are currently satisfied
have = 0
# Sliding window character frequency
window_map = {}
# Left pointer of the window
left = 0
# Store result as (window_length, left_index, right_index)
result = ""
min_len = float('inf')
# Expand window by moving right pointer
for right in range(len(s)):
char = s[right]
# Add right character to window map
window_map[char] = window_map.get(char, 0) + 1
# Check if this char now meets t's requirement
if char in t_map and window_map[char] == t_map[char]:
have += 1
# Shrink from left as long as window is valid
while have == need:
window_len = right - left + 1
# Update result if this window is smaller
if window_len < min_len:
min_len = window_len
result = s[left : right + 1]
# Remove left character to try shrinking
left_char = s[left]
window_map[left_char] -= 1
# If removing breaks a requirement, decrease 'have'
if left_char in t_map and \
window_map[left_char] < t_map[left_char]:
have -= 1
# Move left pointer forward
left += 1
return result
# ── Test it ──
if __name__ == "__main__":
print(min_window_substring("ADOBECODEBANC", "ABC")) # "BANC"
print(min_window_substring("a", "a")) # "a"
print(min_window_substring("a", "b")) # ""
print(min_window_substring("aa", "aa")) # "aa"
Explanation of the Program
Let's walk through every important piece of this solution:
🗂️ Building t_map
t_map = Counter(t) counts how many times each character appears in t. For example, if t = "ABC", then t_map = {'A': 1, 'B': 1, 'C': 1}. This is our target to match.
📌 The need and have variables
need tells us how many unique characters of t we must have fully covered. have keeps count of how many we've actually satisfied so far. When have == need, our window is valid.
➡️ Expanding the Window
We move the right pointer forward one step at a time, adding each new character to window_map. If the count of that character in the window equals the count required by t_map, we increase have.
⬅️ Shrinking the Window
Once have == need, the window is valid. Now we try to make it smaller by moving left forward. Each time we remove a character from the left, we check if that breaks any requirement — if yes, we reduce have and stop shrinking until the right pointer moves again.
💾 Tracking the Answer
Every time we find a valid window (when have == need), we compare its size to min_len. If it's smaller, we update our result with the current window using s[left : right + 1].
🚫 Edge Cases Handled
If s or t is empty, we return "" immediately. If t has characters not in s, the have variable never reaches need, so we return "".
Time Complexity
In the sliding window approach, each character in s is visited at most twice — once when the right pointer adds it, and once when the left pointer removes it. This makes the total time O(n + m) which is very efficient even for large strings.
The brute force approach would check every possible substring of s and verify if it contains all characters of t, leading to a slow O(n² × m) time — completely impractical for long inputs.
Space Complexity
We use two HashMaps: t_map which holds at most m entries (characters from t), and window_map which holds characters from the current window. Since we only deal with English letters, the total unique characters is bounded by 52 (a–z, A–Z), so space is effectively O(1) in practice — or O(m + k) formally.
Test Cases with Expected Output
"ADOBECODEBANC""ABC""BANC""aa""aa""aa""a""b""""a""a""a""aaflslflsldkalskaaa""aaa""aaa"Common Interview Questions on Minimum Window Substring
- What is the Minimum Window Substring problem and how is it different from finding all substrings?
- Why is the sliding window technique more efficient than brute force for this problem?
- How do you handle duplicate characters in the target string
t? - What is the time complexity of your solution and why?
- What happens if
tis longer thans? How does your code handle it? - Can you solve this without using a HashMap? What would the trade-offs be?
- How would you modify your solution to return all minimum windows instead of just one?
- What edge cases should you always check for before coding this problem?
💡 Pro tip: In interviews, always mention the edge cases first — empty strings, no valid window, and duplicate characters. This shows the interviewer that you think carefully before coding.
Conclusion
The Minimum Window Substring problem is a perfect example of how the sliding window technique turns a slow O(n²) brute force into a blazing-fast O(n + m) solution. The key idea is simple: expand the window to find a valid match, then shrink it from the left to minimize the size — all while tracking character frequencies using a HashMap.
Once you understand this pattern, you'll find it shows up in many other problems too — like "Longest Substring Without Repeating Characters", "Longest Substring with At Most K Distinct Characters", and more. Mastering this one problem gives you a serious edge in coding interviews.
Practice it, break it, test it with weird inputs, and you'll have it locked in for good. Happy coding! 🚀
Frequently Asked Questions
What is the Minimum Window Substring problem?
It's a classic string problem where you're given two strings s and t. You need to find the shortest continuous part (substring) of s that contains every character of t — including duplicates — in any order.
What technique is used to solve it efficiently?
The sliding window technique is used. Two pointers — left and right — define a window that expands to include characters and shrinks to minimize the window size whenever all required characters are present.
Why use a HashMap (dictionary) in this solution?
We use two HashMaps to store character frequencies: one for the target string t and one for the current window. This lets us check in O(1) time whether a character's count in the window satisfies the requirement from t.
What is the time complexity of the sliding window solution?
The time complexity is O(n + m) where n is the length of s and m is the length of t. Each character in s is processed at most twice — once by the right pointer and once by the left pointer.
What does the function return if no valid window is found?
It returns an empty string "". This happens when t contains characters that are not present in s, or when t has more occurrences of a character than s does.
How are duplicate characters in t handled?
The frequency maps handle duplicates automatically. For example, if t = "aab", then t_map = {'a': 2, 'b': 1}. The window must contain at least 2 'a's and 1 'b' before it is considered valid. The have counter only increments when the window's count exactly matches the required count.
Is this problem available on LeetCode?
Yes! It's LeetCode Problem #76 — "Minimum Window Substring" — and it's labeled as Hard. However, with a solid understanding of the sliding window pattern, it becomes very approachable.