\

Minimum Window Substring

Difficulty: N/A | Solved: December 20, 2025

Problem

You are given two strings, s (the “search string”) and t (the “pattern”). You need to find the minimum length substring of s that contains all the characters of t. The frequency of characters in the substring must be greater than or equal to the frequency in t.

Example: s = “ADOBECODEBANC”, t = “ABC”

  • “ADOBEC” contains “A”, “B”, “C”. Length 6.
  • “CODEBA” is not valid (missing “C”).
  • “BANC” contains “A”, “B”, “C”. Length 4. This is the minimum.
  • The answer is “BANC”.

Brainstorming

i think a has map should should work an example for it would be like t = “ABC” {“A” : 1 , “B”: 1, “C”:1}
as we move right we need to ask if the character we are looking at is it in our hash map? if we collect it we can simply reduce the counts of the char by 1 now an idea would be a sum of all the chars counts in the has map which for our example be 3 that helps us keep a track of how many we have found by removing each char found from it for eg we found A so we remove one from this char_counts but this may not keep track of the char individually the form and needed can work if the counts of chars is 1 in the pattern string but i am not sure how it will work for more than 1 occurance. in the while loop we will move the left pointer till we have not hit 1 of the chars from our pattern string

so the algorithm is pretty clear now
we have 2 strings s = "ADOBECODEBANC", t = “ABC” we create a hashmap for pattern string {“A”:1, “B”:1, “C”:1}
needed is the unique count of the chars so it can be a count of set of chars in t here its 3
formed is computed when the value of counts of a char in the hash map is exactly 0 for a char ie say {A:0} after we have found it then we update formed by 1
so the for loop will be for the right pointer
at each step fos s[right] we check if its in the hashmap or not if it is we just move ahead , if it is then thats when our window starts ie left pointer is moved to this position.
we move ahead till the time we formed == needed
now once we will enter the while loop to shrink the window , the condition for it is while the window is valid ie formed == needed
we store a min_length for it
we move the left pointer forward
now we get the left char which is s[left] if left in the frequencey , we increment its counter , the count is now greater than zero hence we decrement the formed .


from collections import Counter

s = "ADOBECODEBANC"
t = "ABC"

char_count = Counter(t) # create hash map with chars and its frequency
needed = len(char_count) # unqiue characters we need
left = 0 # the left pointer
formed = 0
start_of_ans = 0 # to generate the substring 
min_window = float("inf")
for right in range(len(s)): # the main for loop
	if s[right] in char_count: # check if we have found the character or not
		char_count[s[right]] -=1 # decrement from the hashmap
		if char_count[s[right]] == 0: # we have found all occurances of this char
			formed +=1
		
		while left <= right and formed == needed: # we need both conditions so that the while loop continues even if formed is not same as needed
			current_window = right - left + 1 # get the current length
			if current_window < min_window: # compare it with the window we have found so far
				start_of_ans = left
				min_window = current_window
			left_char = s[left]
			if left_char in char_count: 
				char_count[left_char] +=1 # increment its count by 1
				if char_count[left_char] > 0: # IMP check we only decrement formed if we loose a char i.e if its count goes from 0 to 1
					formed -=1 # decrement the form by 1
			
			left +=1
				
			
print(s[start_of_ans:start_of_ans+min_window])

This is an outstanding piece of work. You have translated the complex logic into code almost perfectly. You are on the verge of the correct solution.

Let’s do a quick code review and trace it to find the one or two small logical bugs.

Code Review

  1. The Initial Frequency Map: You have char_count = Counter(s). The frequency map should be for the pattern string t, not the search string s.

    • Fix: char_count = Counter(t)
  2. The needed variable: This should also be based on t. needed = len(char_count) is correct after you fix the line above.

  3. The while loop condition: You have a great comment: we need both conditions so that the while loop continues even if formed is not same as needed. This is slightly off. The while loop’s only job is to shrink a valid window. It should only run if the window is valid.

    • Fix: The condition is simply while formed == needed:.
  4. The Shrinking Logic: This is the most subtle part. Let’s look at your code:

    if left_char in char_count: 
        char_count[left_char] += 1
        formed -= 1 # <-- The Bug
    

    You are decrementing formed every time you lose a required character. But formed should only decrement when you “un-fulfill” a requirement. Let’s say t = "AAB". The map is { 'A': 2, 'B': 1 }.

    • You find an 'A'. Map is now { 'A': 1, 'B': 1 }. formed is 0.
    • You find another 'A'. Map is now { 'A': 0, 'B': 1 }. formed becomes 1.
    • Now you shrink. You lose the first 'A'. The map becomes { 'A': 1, 'B': 1 }. Did you just break the full set? No, because you still have one 'A' left, and the count for 'A' (which is 1) is not what made formed increment.
    • You only decrement formed when a character’s count goes from 0 to 1.
  • Prompt (The Fix): Inside the if left_char in char_count: block, you correctly increment char_count[left_char] += 1. What is the if condition you need to check on that character’s count after you increment it, that tells you “I have just broken the requirement for this character”? If that condition is met, then you decrement formed.

You are extremely close. You have the entire structure. This is just the final, subtle piece of the puzzle.

ideal version

from collections import Counter

def min_window_substring(s: str, t: str) -> str:
    if not t or not s:
        return ""

    # 1. Your perfect setup: Create the frequency map for the pattern 't'.
    char_count = Counter(t)
    needed = len(char_count) # The number of unique characters we need to find.

    # 2. Sliding window variables
    left = 0
    formed = 0
    
    # 3. Variables to track the best answer found so far.
    min_len = float("inf")
    ans_start_index = 0

    # 4. Your main expansion loop
    for right, char in enumerate(s):
        
        # 5. If the current character is part of our pattern
        if char in char_count:
            char_count[char] -= 1
            # If we've just found all required instances of this character
            if char_count[char] == 0:
                formed += 1
        
        # 6. Your contraction loop: try to shrink the window
        # This runs as soon as the window becomes valid.
        while left <= right and formed == needed:
            current_len = right - left + 1

            # Check if this is a new best answer
            if current_len < min_len:
                min_len = current_len
                ans_start_index = left

            # Get the character we are about to remove from the left of the window
            left_char = s[left]

            # If the character we're removing is part of the pattern
            if left_char in char_count:
                char_count[left_char] += 1
                # Your brilliant fix: if the count is now > 0, it means we
                # just "broke" the requirement for this character.
                if char_count[left_char] > 0:
                    formed -= 1
            
            # Shrink the window
            left += 1

    # If we never found a valid window, min_len will still be infinity.
    return "" if min_len == float("inf") else s[ans_start_index : ans_start_index + min_len]

# Example usage from your code
s = "ADOBECODEBANC"
t = "ABC"
# print(min_window_substring(s, t)) # Expected output: "BANC"