150NeetCode 150

19.Minimum Window Substring

HardBlind 75Sliding Window ยท 5/6

Problem

Given strings s and t, return the shortest substring of s that contains every character of t, including duplicates. If no such substring exists, return the empty string "".

The test data guarantees the answer is unique.

Examples

Input:  s = "ADOBECODEBANC", t = "ABC"
Output: "BANC"
Explanation: "BANC" contains 'A', 'B' and 'C' and no shorter window does.
Input:  s = "a", t = "a"
Output: "a"
Input:  s = "a", t = "aa"
Output: ""
Explanation: t needs two 'a's but s has only one.

Constraints

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

๐Ÿ’ก Key Insight

Use a variable-size sliding window. Expand right until the window covers t; then shrink left as far as possible while it still covers t, recording the smallest valid window. To test "covers t" in O(1), keep a single counter missing = how many characters of t (with multiplicity) the window still lacks; it only changes when a needed character enters or leaves.

Brute Force

TimeO(mยฒ ยท 128)SpaceO(1)

Intuition

Check every substring s[i..j]: count its characters and test whether each character of t appears at least as many times. Keep the shortest one that passes. Extending j incrementally avoids recounting from scratch.

Algorithm

  1. Build need[128] from t.
  2. For each start i: reset have[128]; for each end j >= i, add s[j] to have.
  3. If have[c] >= need[c] for every c, record s[i..j] if shorter than the best, then break (longer windows from i cannot be shorter).
  4. Return the best window or "".

Visualization

s = "ADOBECODEBANC", t = "ABC"

i=0: A D O B E C        covers at j=5 โ†’ "ADOBEC" (6)
i=1: D O B E C O D E B A covers at j=10 โ†’ length 10
...
i=9: B A N C            covers at j=12 โ†’ "BANC" (4) โœ“ shortest

Code

java
class Solution {
    public String minWindow(String s, String t) {
        int[] need = new int[128];
        for (char c : t.toCharArray()) {
            need[c]++;
        }
        int bestStart = 0, bestLen = Integer.MAX_VALUE;
        for (int i = 0; i < s.length(); i++) {
            int[] have = new int[128];
            for (int j = i; j < s.length(); j++) {
                have[s.charAt(j)]++;
                if (covers(have, need)) {
                    if (j - i + 1 < bestLen) {
                        bestLen = j - i + 1;
                        bestStart = i;
                    }
                    break; // extending j further only makes it longer
                }
            }
        }
        return bestLen == Integer.MAX_VALUE ? "" : s.substring(bestStart, bestStart + bestLen);
    }

    private boolean covers(int[] have, int[] need) {
        for (int c = 0; c < 128; c++) {
            if (have[c] < need[c]) {
                return false;
            }
        }
        return true;
    }
}

Complexity

  • Time: O(mยฒ ยท 128) โ€” O(mยฒ) windows, each checked against 128 counters.
  • Space: O(1) โ€” fixed-size count arrays.

Complexity Comparison

Approach Time Space
Brute Force O(mยฒ ยท 128) O(1)
Sliding Window + full check O(128 ยท m) O(1)
Sliding Window + missing counter O(m + n) O(1)

Common Pitfalls

  • Ignoring duplicates in t: t = "AA" needs two 'A's โ€” a set of required letters is not enough.
  • Decrementing missing for every character that enters, even surplus ones โ€” only decrement when need[c] > 0 before the decrement.
  • Building s.substring(...) inside the loop for every candidate; store (start, length) and build the string once at the end.
  • Using a 26-slot array โ€” s and t mix upper and lowercase letters, so index by the raw char (128 slots) or use 52.