How to Explain Minimum Window Substring in a Coding Interview (Step-by-Step)
Minimum Window Substring is the hardest sliding window problem you’ll encounter. The window mechanics aren’t new — expand right, shrink left — but the formed and required counters are the trick that makes it efficient. Nail those two variables and the rest follows naturally.
In this post, you’ll learn exactly how to walk an interviewer through this problem: the two-counter trick, the expand-then-shrink loop, a full step-by-step trace, and the edge cases interviewers always probe.
Problem Statement
Given two strings s and t of lengths m and n, return the minimum window substring of s such that every character in t (including duplicates) is included in the window. If there is no such substring, return the empty string "".
Examples
Output: “BANC”
Explanation: “BANC” is the smallest window containing A, B, and C
Output: “a”
Output: “” → s has only one ‘a’, t needs two
Constraints
m == s.length,n == t.length1 <= m, n <= 10⁵sandtconsist of uppercase and lowercase English letters
What Is the Problem?
“I need to find the smallest contiguous substring ofsthat contains every character fromt— including duplicates. Ifthas two A’s, the window must contain at least two A’s. If no such window exists, return an empty string.”
Stressing duplicates must be covered upfront shows you’ve read the constraints carefully.
Step 1: Start With the Brute Force (Then Dismiss It)
Generate all substrings of s, check each one covers t
Time: O(m² · n) — O(m²) substrings × O(n) check
Space: O(n)
Way too slow for m, n = 10⁵
Expand right until valid, shrink left to minimise, track best
Time: O(m + n)
Space: O(m + n)
Each character added and removed at most once
The Key Insight — Expand Then Shrink
Use a variable-size sliding window. Expand the right pointer until the window contains all characters of t. Once valid, try to shrink from the left to minimise the window — record the window if it’s the smallest seen. Keep shrinking until the window becomes invalid again. Then expand right again. Repeat until the right pointer reaches the end.
The challenge is efficiently knowing when a window is valid without scanning the full frequency map each time. That’s what formed and required solve.
The formed / required Trick — The Heart of This Problem
The number of unique characters in t that need to be satisfied. Computed once at the start. e.g. for t = "ABC", required = 3.
The number of unique characters currently in the window that meet their required frequency. When formed == required, the window is valid.
“When the window frequency of a character equals its frequency int, I incrementformed. When I shrink the window and a character’s count drops below the required frequency, I decrementformed. Checkingformed == requiredis O(1) — no need to scan the map.”
Step 3: Explain the Algorithm in Plain English
“I build a frequency map fort. I use two pointers —leftandright— both starting at 0. I moverightforward, adding characters to the window’s frequency map and updatingformedwhen a character reaches its required count. Whenformed == required, the window is valid — I record it if it’s smaller than the best seen, then try to shrink by movingleftforward. I keep shrinking while the window stays valid. Once it becomes invalid, I expand right again.”
Step 4: Talk Through the Code
from collections import Counter
class Solution:
def minWindow(self, s: str, t: str) -> str:
if not t or not s:
return ""
t_count = Counter(t) # Frequency map for t
required = len(t_count) # Unique chars in t that must be satisfied
window_count = {} # Frequency map for current window
formed = 0 # Unique chars in window meeting required freq
best = float('inf'), 0, 0 # (window_size, left, right) of best window
left = 0
for right in range(len(s)):
char = s[right]
window_count[char] = window_count.get(char, 0) + 1
# Check if this char now meets its required frequency
if char in t_count and window_count[char] == t_count[char]:
formed += 1
# Try to shrink the window while it remains valid
while left <= right and formed == required:
# Update best if this window is smaller
if right - left + 1 < best[0]:
best = (right - left + 1, left, right)
# Remove the leftmost character
left_char = s[left]
window_count[left_char] -= 1
if left_char in t_count and window_count[left_char] < t_count[left_char]:
formed -= 1 # Window no longer satisfies this char
left += 1
return "" if best[0] == float('inf') else s[best[1]: best[2] + 1]
Point out the best tuple — storing (size, left, right) together avoids recomputing the window string until the very end, which keeps the inner loop clean.
Visual Walkthrough
Tracing s = "ADOBECODEBANC", t = "ABC". required = 3:
Full key steps trace:
| Action | L | R | Window | formed | size | best |
|---|---|---|---|---|---|---|
| 0 | 5 | ADOBEC | 3 | 6 | "ADOBEC"(6) | |
| shrink | 1 | 5 | DOBEC | 2 | — | "ADOBEC"(6) |
| 1 | 10 | BECODEBA | 3 | 8 | "ADOBEC"(6) | |
| shrink ×5 | 9 | 10 | BA | 2 | — | "ADOBEC"(6) |
| 9 | 12 | BANC | 3 | 4 | "ADOBEC"(6) | |
| record ★ | 9 | 12 | BANC | 3 | 4 | "BANC"(4) ★ |
| shrink | 10 | 12 | ANC | 2 | — | "BANC"(4) |
Step 5: Highlight Edge Cases
"e.g.t = "AA"— the window must contain at least 2 A's. The frequency map storest_count['A'] = 2, soformedonly increments whenwindow_count['A']reaches 2. One A in the window is not enough."
"Iftcontains a character not ins,formedcan never equalrequired.beststays atinfinityand we return"". The final check handles this cleanly."
"If s == t, the only valid window is the entire string. The algorithm will find it — expand right to the end, try to shrink but can't without losing validity, record the full string."
"The constraints say both strings contain uppercase and lowercase letters —'A'and'a'are different characters. The HashMap handles this naturally since they have different ASCII values. No special casing needed."
"Iflen(t) > len(s), a valid window is impossible. The early return check catches this — we can add it explicitly for clarity, or rely onformednever reachingrequired."
Step 6: State Time and Space Complexity
Let m = len(s), n = len(t).
| Dimension | Complexity | Reason |
|---|---|---|
| Time | O(m + n) | O(n) to build t_count, O(2m) for sliding window — each char added and removed once |
| Space | O(m + n) | Two frequency maps — at most O(52) unique chars each = O(1) if alphabet is fixed |
"In the worst case each character ofsis added once whenrightmoves and removed once whenleftmoves — so the window processes at most2moperations total. With theformedcounter, the validity check inside the shrink loop is O(1). Overall O(m + n)."
Interview Checklist for Minimum Window Substring
- Restate — "smallest substring of s containing all chars of t including duplicates"
- Mention brute force O(m² · n) and dismiss it
- State the sliding window pattern — expand right until valid, shrink left to minimise
- Explain
required— number of unique chars in t to satisfy - Explain
formed— counts satisfied unique chars, checked in O(1) - Explain when formed increments and decrements
- Handle duplicate chars in t — frequency must be fully met
- Handle no valid window — return empty string
- Handle case sensitivity — uppercase and lowercase are distinct
- State O(m + n) time and O(m + n) space
- Ask before coding, don't just dive in
Related Problems to Practice
If you can solve Minimum Window Substring cleanly, try these next:
