Minimum Window Substring

Expand to satisfy the requirement, then contract to minimize.

Sliding Window Trace

t = ABC. Nothing taken yet, so missing = 3.
StopSliding windowExport
st
  1. LA0
  2. D1
  3. O2
  4. B3
  5. E4
  6. C5
  7. O6
  8. D7
  9. E8
  10. B9
  11. A10
  12. N11
  13. C12

have / need

  • A0/1
  • B0/1
  • C0/1
missing3bestnone
  • CH1 left
  • CH2 right

Problem

Given two strings s and t, find the shortest contiguous part of s that contains all characters from t with the required multiplicities. Return the empty string if no substring can satisfy the requirement.

Examples

Input: s = "ADOBECODEBANC", t = "ABC"

Output: "BANC"

The window BANC contains A, B, and C, and no shorter valid window exists.

Input: s = "a", t = "aa"

Output: ""

The source contains only one a, but two are required.

The Goal

Find the shortest stretch of s that contains every character of t, repeats included. Extra characters are fine. If no stretch works, return "".

For t = "ABC", you need one A, one B and one C. ADOBEC qualifies, extra letters and all. For t = "AABC", you'd need two As.

Every count has to be met

A window is valid when it holds at least as many of each character as t does. Extras don't hurt.

Expand, Then Contract

Move right until the window covers t. Then move left for as long as it still covers t, recording each window on the way. When it stops covering t, go back to moving right.

The shortest window turns up while shrinking. After ADOBEC, dropping the A breaks it, so right has to find another A.

Move right until every required character is in.

Read through ADOBEC to cover ABC.

Counting What's Missing

need holds the counts from t, and window holds the counts in the current stretch. missing starts at len(t). It drops when an incoming character fills a count that's still short, and rises when a dropped character leaves one short.

Code
need = frequency(t)
left = 0, missing = length(t)
best = empty
for right from 0 to length(s) - 1:
    add s[right] and update missing
    while missing == 0:
        record the smallest valid window
        remove s[left] and update missing
        left += 1
return best

missing == 0 tells you the window is valid without rescanning it.

StateMeaningFor t = "ABC"
needCounts in tA: 1, B: 1, C: 1
windowCounts in the windowWhat the window covers now
missingRequired characters not yet covered3 at the start, 0 when valid

Walkthrough

Run it on ADOBECODEBANC with t = "ABC".

right reaches the C at 5, and ADOBEC covers t: length 6. Dropping its A breaks it. The next A, at 10, makes the window valid again, and it shrinks to CODEBA, still length 6.

Then the last C arrives. ODEBANC shrinks past O, D and E to BANC, length 4. That's the answer.

Checkpoint · predict the next step

Step 11 of 35 on the stage: Take 'D' at 7. t has no 'D', so missing stays 1.

window "DOBECOD" (left 1, right 7) · missing 1 · best "ADOBEC"

What does step 12 show?
Step through it on the stage

Pitfalls

  • Growing without shrinking finds a valid window, not the shortest. With s = "BA" and t = "A", you'd stop at BA instead of A.
  • Shrink with while, not if. One step of right can free several letters.
  • Record before you drop the left character.
  • An extra copy isn't a shortfall. Only window[ch] < need[ch] raises missing.

What should happen as soon as the window becomes valid?

Complexity

There's a while inside the for, but both pointers only move forward. Each character enters the window once and leaves at most once, so time is O(|s| + |t|), counting the pass that builds need.

The maps hold one count per character, so space is O(alphabet), which is constant for a fixed alphabet.

Two forward pointers

Time
O(n)
Space
O(alphabet)
Where the time goes
  • Build need from tOne count per character of t.O(|t|)
  • Expand with rightEach character enters once.dominantO(n)
  • Contract with leftEach character leaves at most once.dominantO(n)
  • Frequency mapsConstant for a fixed alphabet.O(alphabet)
How it grows
input size n →opsO(n²)O(n log n)O(n)O(log n)O(1)
Roughly what that costs
n101001,00010,000
O(n)101001K10K

Linear, because neither pointer ever moves back.

In short

Count the characters you need. Expand the right edge until the window contains all of them, then contract the left edge as far as possible while still valid, recording the smallest such window. A 'need' counter lets you check validity in O(1) per step.

TimeO(n)SpaceO(alphabet)

Common mistake

Only expanding, never contracting — the minimum window requires shrinking whenever the constraint is still met.