Minimum Window Substring

hard

Given two strings s and t of lengths m and n respectively, 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 "".

A window substring is a contiguous sequence of characters within s. The answer is guaranteed to be unique when one exists. The characters in t may appear more than once, and the window must contain at least as many of each character.

Examples

Example 1

  • Input: s = "ADOBECODEBANC", t = "ABC"
  • Output: "BANC"
  • Explanation: The minimum window substring "BANC" includes 'A', 'B', and 'C' from string t.

Example 2

  • Input: s = "a", t = "a"
  • Output: "a"
  • Explanation: The entire string s is the minimum window.

Example 3

  • Input: s = "a", t = "aa"
  • Output: ""
  • Explanation: Both 'a's from t must be included in the window. Since s only has one 'a', it is impossible and we return an empty string.

Constraints

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

Approaches

2 worked solutions ranked from straightforward to optimal — each with intuition, algorithm, complexity, and verified code in C++, Java, Python, and Go.

Approach 1

Brute Force

Intuition

The most direct approach is to check every possible substring of s and determine if it contains all characters of t. For each valid substring, we track the one with the minimum length. To check if a substring contains all characters of t, we compare character frequency counts. This approach is easy to understand but very slow for large inputs.

Algorithm

  1. Build a frequency map of characters in t.
  2. For each starting index i in s, iterate over each ending index j from i to m - 1.
  3. Maintain a running frequency count of characters in the window [i, j].
  4. Check if the window contains all characters of t by comparing frequency counts.
  5. If valid and the window length is smaller than the current minimum, update the result.
  6. Return the minimum window substring, or an empty string if none was found.
TimeO(m^2 * n) where m is the length of s and n is the size of the character setSpaceO(m + n)

Code (C++ · Java · Python · Go)

C++17
class Solution {
public:
    string minWindow(string s, string t) {
        if (t.size() > s.size()) return "";

        unordered_map<char, int> tFreq;
        for (char c : t) tFreq[c]++;

        int minStart = 0, minLen = INT_MAX;

        for (int i = 0; i < s.size(); i++) {
            unordered_map<char, int> windowFreq;
            for (int j = i; j < s.size(); j++) {
                windowFreq[s[j]]++;

                bool valid = true;
                for (auto& [ch, count] : tFreq) {
                    if (windowFreq[ch] < count) {
                        valid = false;
                        break;
                    }
                }

                if (valid && j - i + 1 < minLen) {
                    minLen = j - i + 1;
                    minStart = i;
                    break;
                }
            }
        }

        return minLen == INT_MAX ? "" : s.substr(minStart, minLen);
    }
};
Java 21
class Solution {
    public String minWindow(String s, String t) {
        if (t.length() > s.length()) return "";

        Map<Character, Integer> tFreq = new HashMap<>();
        for (char c : t.toCharArray()) tFreq.merge(c, 1, Integer::sum);

        int minStart = 0, minLen = Integer.MAX_VALUE;

        for (int i = 0; i < s.length(); i++) {
            Map<Character, Integer> windowFreq = new HashMap<>();
            for (int j = i; j < s.length(); j++) {
                windowFreq.merge(s.charAt(j), 1, Integer::sum);

                boolean valid = true;
                for (var entry : tFreq.entrySet()) {
                    if (windowFreq.getOrDefault(entry.getKey(), 0) < entry.getValue()) {
                        valid = false;
                        break;
                    }
                }

                if (valid && j - i + 1 < minLen) {
                    minLen = j - i + 1;
                    minStart = i;
                    break;
                }
            }
        }

        return minLen == Integer.MAX_VALUE ? "" : s.substring(minStart, minStart + minLen);
    }
}
Python 3.12
def minWindow(s: str, t: str) -> str:
    from collections import Counter

    if len(t) > len(s):
        return ""

    t_freq = Counter(t)
    min_start, min_len = 0, float("inf")

    for i in range(len(s)):
        window_freq = Counter()
        for j in range(i, len(s)):
            window_freq[s[j]] += 1

            if all(window_freq[ch] >= count for ch, count in t_freq.items()):
                if j - i + 1 < min_len:
                    min_len = j - i + 1
                    min_start = i
                break

    return "" if min_len == float("inf") else s[min_start:min_start + min_len]
Go 1.26
import "math"

func minWindow(s string, t string) string {
    if len(t) > len(s) {
        return ""
    }

    tFreq := make(map[byte]int)
    for i := 0; i < len(t); i++ {
        tFreq[t[i]]++
    }

    minStart, minLen := 0, math.MaxInt32

    for i := 0; i < len(s); i++ {
        windowFreq := make(map[byte]int)
        for j := i; j < len(s); j++ {
            windowFreq[s[j]]++

            valid := true
            for ch, count := range tFreq {
                if windowFreq[ch] < count {
                    valid = false
                    break
                }
            }

            if valid && j-i+1 < minLen {
                minLen = j - i + 1
                minStart = i
                break
            }
        }
    }

    if minLen == math.MaxInt32 {
        return ""
    }
    return s[minStart : minStart+minLen]
}
Approach 2

Sliding Window

Intuition

We use two pointers to maintain a window over s. We expand the right pointer to include characters until the window satisfies the requirement (contains all characters of t). Then we shrink from the left to find the smallest valid window. We track how many characters from t are fully satisfied using a counter. When all required characters are met, we try to minimize the window by moving the left pointer.

Algorithm

  1. Build a frequency map for characters in t. Track the number of unique characters that need to be matched (required).
  2. Initialize left = 0, a counter formed = 0 (number of characters fully matched), and variables to track the best window.
  3. Expand the window by moving right. For each s[right], update the window frequency map.
  4. If the window frequency of s[right] matches the required frequency in t, increment formed.
  5. While formed equals required (window is valid), try to shrink from the left. Update the minimum window if the current window is smaller. Decrement the frequency of s[left] and if it drops below the required count, decrement formed. Move left forward.
  6. Return the minimum window substring, or an empty string if none exists.
TimeO(m + n) where m is the length of s and n is the length of tSpaceO(m + n)

Code (C++ · Java · Python · Go)

C++17
class Solution {
public:
    string minWindow(string s, string t) {
        if (t.size() > s.size()) return "";

        unordered_map<char, int> tFreq, windowFreq;
        for (char c : t) tFreq[c]++;

        int required = tFreq.size();
        int formed = 0;
        int left = 0;
        int minLen = INT_MAX, minStart = 0;

        for (int right = 0; right < s.size(); right++) {
            windowFreq[s[right]]++;

            if (tFreq.count(s[right]) && windowFreq[s[right]] == tFreq[s[right]]) {
                formed++;
            }

            while (formed == required) {
                if (right - left + 1 < minLen) {
                    minLen = right - left + 1;
                    minStart = left;
                }

                windowFreq[s[left]]--;
                if (tFreq.count(s[left]) && windowFreq[s[left]] < tFreq[s[left]]) {
                    formed--;
                }
                left++;
            }
        }

        return minLen == INT_MAX ? "" : s.substr(minStart, minLen);
    }
};
Java 21
class Solution {
    public String minWindow(String s, String t) {
        if (t.length() > s.length()) return "";

        Map<Character, Integer> tFreq = new HashMap<>();
        for (char c : t.toCharArray()) tFreq.merge(c, 1, Integer::sum);

        Map<Character, Integer> windowFreq = new HashMap<>();
        int required = tFreq.size();
        int formed = 0;
        int left = 0;
        int minLen = Integer.MAX_VALUE, minStart = 0;

        for (int right = 0; right < s.length(); right++) {
            char c = s.charAt(right);
            windowFreq.merge(c, 1, Integer::sum);

            if (tFreq.containsKey(c) && windowFreq.get(c).intValue() == tFreq.get(c).intValue()) {
                formed++;
            }

            while (formed == required) {
                if (right - left + 1 < minLen) {
                    minLen = right - left + 1;
                    minStart = left;
                }

                char leftChar = s.charAt(left);
                windowFreq.merge(leftChar, -1, Integer::sum);
                if (tFreq.containsKey(leftChar) && windowFreq.get(leftChar) < tFreq.get(leftChar)) {
                    formed--;
                }
                left++;
            }
        }

        return minLen == Integer.MAX_VALUE ? "" : s.substring(minStart, minStart + minLen);
    }
}
Python 3.12
from collections import Counter


def minWindow(s: str, t: str) -> str:
    if len(t) > len(s):
        return ""

    t_freq = Counter(t)
    window_freq = Counter()
    required = len(t_freq)
    formed = 0
    left = 0
    min_len = float("inf")
    min_start = 0

    for right in range(len(s)):
        window_freq[s[right]] += 1

        if s[right] in t_freq and window_freq[s[right]] == t_freq[s[right]]:
            formed += 1

        while formed == required:
            if right - left + 1 < min_len:
                min_len = right - left + 1
                min_start = left

            window_freq[s[left]] -= 1
            if s[left] in t_freq and window_freq[s[left]] < t_freq[s[left]]:
                formed -= 1
            left += 1

    return "" if min_len == float("inf") else s[min_start:min_start + min_len]
Go 1.26
import "math"

func minWindow(s string, t string) string {
    if len(t) > len(s) {
        return ""
    }

    tFreq := make(map[byte]int)
    for i := 0; i < len(t); i++ {
        tFreq[t[i]]++
    }

    windowFreq := make(map[byte]int)
    required := len(tFreq)
    formed := 0
    left := 0
    minLen := math.MaxInt32
    minStart := 0

    for right := 0; right < len(s); right++ {
        windowFreq[s[right]]++

        if count, ok := tFreq[s[right]]; ok && windowFreq[s[right]] == count {
            formed++
        }

        for formed == required {
            if right-left+1 < minLen {
                minLen = right - left + 1
                minStart = left
            }

            windowFreq[s[left]]--
            if count, ok := tFreq[s[left]]; ok && windowFreq[s[left]] < count {
                formed--
            }
            left++
        }
    }

    if minLen == math.MaxInt32 {
        return ""
    }
    return s[minStart : minStart+minLen]
}

Frequently asked questions

What is the optimal time complexity of Minimum Window Substring?

The most efficient approach in this guide ("Sliding Window") runs in O(m + n) where m is the length of s and n is the length of t time and uses O(m + n) extra space. Earlier brute-force approaches are slower; see all 2 approaches above for the full progression.

What pattern does Minimum Window Substring use?

Minimum Window Substring is a hard-level Sliding Window problem involving String, Sliding Window, Hash Table. Recognizing the pattern is half the work — see the intuition section above for how to spot it.

Can Minimum Window Substring be solved without extra space?

The most space-efficient approach in this guide uses O(m + n) extra space (excluding the input). If you're aiming for in-place — see the trade-off in the algorithm section above — the brute-force approach uses O(m + n), while the optimized version uses O(m + n).

Which programming languages does this Ratta solution support?

Every approach above ships with verified, runnable solutions in C++, Java, Python, and Go. The starter templates in the workspace match the same four languages so you can practice in your interview language of choice.

Related Problems

Original problem: leetcode.com

Loading editor...
s=
"ADOBECODEBANC"
t=
"ABC"
"BANC"