Problem

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 "".

The testcases will be generated such that the answer is unique.

OR

Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).

Example

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 the largest window of s only has one 'a', return empty string.

Similar Problems

Minimum Size Subarray Sum Problem

Note

  • If there is no such window in S that covers all characters in T, return the emtpy string “”.
  • If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.

Solution

The problem is similar to the problem here - Find anagrams of a string in another string.

The problem discussed in this article is a variation of the anagram problem. Instead of finding fixed-length anagram substrings, the goal is to find the minimum-length substring that contains all the characters of the anagram.

The ideal shortest solution would be an anagram of T as a substring of S. For example, if S = "adbacfab" and T = "abc", the answer is "bac", which is an anagram of T and a substring of S. However, consider S = "adobecodebanc" and T = "abc". Here, no anagram of T is a substring of S. Instead, we seek the smallest substring that contains all characters of T. For example, the substrings "adobec""codeba", and "banc" all contain the characters of T, with "banc" being the minimum-length solution.

Method 1 - Brute Force Solution

The naive approach can be: a) Generate all substrings of string1 (“this is a test string”). b) Check if each substring contains all characters of string2 (“tist”). c) Print the smallest substring that includes all characters of string2.

Method 2 - Sliding Window (Using 2 Hashmaps and 2 pointers)

This problem is based on the variable-size sliding window pattern. We can use the sliding window strategy similar to the one discussed in the problem Longest Substring with K distinct characters.

Here is the approach:

  1. Use two dictionaries to count the characters in t and the current window in s.
  2. Expand the window by moving the end pointer.
  3. Contract the window by moving the start pointer when the current window contains all characters of t.
  4. Track the minimum window length and starting index.

Code

Java
class Solution {    
	public String minWindow(String s, String t) {  
	    int n = s.length();  
	  
	    // length of the minimum window substring (smallest substring of s that has all characters of t)  
	    int minLen = Integer.MAX_VALUE;  
	  
	    // start index of the minimum window substring  
	    int minStart = 0;  
	  
	    // stores the count of each character in the current window  
	    Map<Character, Integer> windowMap = new HashMap<>();  
	  
	    // stores the count of each character in the string t  
	    Map<Character, Integer> targetMap = new HashMap<>();  
	  
	    for(int i = 0; i < t.length(); i++) {  
	        char c = t.charAt(i);  
	        targetMap.put(c, targetMap.getOrDefault(c, 0)+1);  
	    }  
	  
	    int windowStart = 0;  
	    for(int windowEnd = 0; windowEnd < n; windowEnd++) {  
	        // Add the next character of the string to the window  
	        char c = s.charAt(windowEnd);  
	        windowMap.put(c, windowMap.getOrDefault(c, 0)+1);  
	  
	        // Keep looking for a smaller window while the current window substring contains all the characters of t  
	        while(containsAll(windowMap, targetMap)) {  
	            if(windowEnd-windowStart+1 < minLen) {  
	                minLen = windowEnd-windowStart+1;  
	                minStart = windowStart;  
	            }  
	  
	            // move the leftmost character out of the window  
	            char leftChar = s.charAt(windowStart);  
	            windowMap.put(leftChar, windowMap.get(leftChar)-1);  
	            if(windowMap.get(leftChar) == 0) {  
	                windowMap.remove(leftChar);  
	            }  
	            windowStart++; // shrink the window  
	        }  
	    }  
	  
	    return minLen == Integer.MAX_VALUE ? "" : s.substring(minStart, minStart + minLen);  
	}  
	  
	private boolean containsAll(Map<Character, Integer> windowCharMap, Map<Character, Integer> substrMap) {  
	    for(Map.Entry<Character, Integer> entry : substrMap.entrySet()) {  
	        char c = entry.getKey();  
	        int count = entry.getValue();  
	  
	        if(!windowCharMap.containsKey(c)) {  
	            return false;  
	        }  
	  
	        if(windowCharMap.get(c) < count) {  
	            return false;  
	        }  
	    }  
	    return true;  
	}
}
Python
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        n = len(s)
        
        # length of the minimum window substring (smallest substring of s that has all characters of t)
        min_len = float('inf')
        
        # start index of the minimum window substring
        min_start = 0
        
        # stores the count of each character in the string t
        target_map = Counter(t)
        
        # stores the count of each character in the current window
        window_map = defaultdict(int)
        
        window_start = 0
        for window_end in range(n):
            # Add the next character of the string to the window
            char = s[window_end]
            window_map[char] += 1

            # Keep looking for a smaller window while the current window substring contains all the characters of t
            while self.contains_all(window_map, target_map):
                if window_end - window_start + 1 < min_len:
                    min_len = window_end - window_start + 1
                    min_start = window_start
                
                # Move the leftmost character out of the window
                left_char = s[window_start]
                window_map[left_char] -= 1
                if window_map[left_char] == 0:
                    del window_map[left_char]
                window_start += 1  # shrink the window
        
        return "" if min_len == float('inf') else s[min_start:min_start + min_len]
    
    def contains_all(self, window_map, target_map):
        for char, count in target_map.items():
            if window_map[char] < count:
                return False
        return True