How to Explain Minimum Window Substring in a Coding Interview (Step-by-Step)

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

LeetCode 76 Hard

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

Example 1
Input: s = “ADOBECODEBANC”, t = “ABC”
Output: “BANC”
Explanation: “BANC” is the smallest window containing A, B, and C
Example 2
Input: s = “a”, t = “a”
Output: “a”
Example 3
Input: s = “a”, t = “aa”
Output: “” → s has only one ‘a’, t needs two

Constraints

  • m == s.length, n == t.length
  • 1 <= m, n <= 10⁵
  • s and t consist of uppercase and lowercase English letters

What Is the Problem?

“I need to find the smallest contiguous substring of s that contains every character from t — including duplicates. If t has 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)

❌ Brute Force

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⁵

✅ Variable Sliding Window

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

💡 Core Insight

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

required

The number of unique characters in t that need to be satisfied. Computed once at the start. e.g. for t = "ABC", required = 3.

formed

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 in t, I increment formed. When I shrink the window and a character’s count drops below the required frequency, I decrement formed. Checking formed == required is O(1) — no need to scan the map.”

Step 3: Explain the Algorithm in Plain English

“I build a frequency map for t. I use two pointers — left and right — both starting at 0. I move right forward, adding characters to the window’s frequency map and updating formed when a character reaches its required count. When formed == required, the window is valid — I record it if it’s smaller than the best seen, then try to shrink by moving left forward. 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:

Key window states — blue = in window, yellow = required char, green = best window found
Expand right → first valid window at right=5
A
D
O
B
E
C
O
D
E
B
A
N
C
Window: "ADOBEC" | formed=3=required | size=6 → record as best
Shrink left → remove A, window invalid
A
D
O
B
E
C
O
D
E
B
A
N
C
Window: "DOBEC" | formed=2 < 3 → invalid, expand right again
Expand right → next valid window at right=10
A
D
O
B
E
C
O
D
E
B
A
N
C
Window: "BECODEBA" | formed=3=required | size=8 → not better
Shrink left aggressively → best window = "BANC"
A
D
O
B
E
C
O
D
E
B
A
N
C
Window: "BANC" | formed=3=required | size=4 → new best ★

Full key steps trace:

ActionLRWindowformedsizebest
expand05ADOBEC36"ADOBEC"(6)
shrink15DOBEC2"ADOBEC"(6)
expand110BECODEBA38"ADOBEC"(6)
shrink ×5910BA2"ADOBEC"(6)
expand912BANC34"ADOBEC"(6)
record ★912BANC34"BANC"(4) ★
shrink1012ANC2"BANC"(4)

Step 5: Highlight Edge Cases

1 t has duplicate characters
"e.g. t = "AA" — the window must contain at least 2 A's. The frequency map stores t_count['A'] = 2, so formed only increments when window_count['A'] reaches 2. One A in the window is not enough."
2 No valid window exists
"If t contains a character not in s, formed can never equal required. best stays at infinity and we return "". The final check handles this cleanly."
3 s equals t
"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."
4 Case sensitivity
"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."
5 t longer than s
"If len(t) > len(s), a valid window is impossible. The early return check catches this — we can add it explicitly for clarity, or rely on formed never reaching required."

Step 6: State Time and Space Complexity

Let m = len(s), n = len(t).

DimensionComplexityReason
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 of s is added once when right moves and removed once when left moves — so the window processes at most 2m operations total. With the formed counter, 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

If you can solve Minimum Window Substring cleanly, try these next:

💡 Practicing your explanations out loud is just as important as writing correct code. The best candidates aren't the ones who solve it fastest — they're the ones who make the interviewer feel like they're thinking alongside them.

Scroll to Top