Loading problems...
TL;DR: The optimal LeetCode 76 solution utilizes the Sliding Window pattern to dynamically expand a range to cover all required characters and contract it to minimize the length, achieving linear time complexity.
The "Minimum Window Substring" problem asks us to find the shortest contiguous substring within a source string s that contains every character from a target string t, including duplicates. If no such substring exists, we must return an empty string. If multiple substrings satisfy the condition with the same minimum length, the problem guarantees the answer is unique or implies returning any valid one (though the test cases often ensure uniqueness).
This is a classic interview problem that tests your ability to manage state within a linear scan, making it a staple at top-tier tech companies.
The naive approach involves generating all possible substrings of s and checking if each one contains all characters from t.
s[i:j].t.Pseudo-code:
textmin_len = infinity result = "" for i from 0 to length(s): for j from i to length(s): sub = s[i...j] if contains_all_chars(sub, t): if length(sub) < min_len: min_len = length(sub) result = sub return result
Complexity Analysis: The time complexity is . There are substrings, and validating each substring takes (or where is the alphabet size). Given that the constraints allow lengths up to , an or even approach will immediately result in a Time Limit Exceeded (TLE) error.
The transition from brute force to the optimal solution relies on the observation that we do not need to re-scan the entire string for every new starting position.
The core insight is to maintain a "window" defined by two pointers, left and right. We can determine the validity of the current window by maintaining a frequency map of characters inside it.
right pointer to include characters until the window becomes "valid" (contains all characters from t).left pointer forward to shrink the window, updating the minimum length, until the window becomes invalid again.Visual Description:
Imagine the s string as a fixed tape. We place a flexible frame (the window) over it.
t.
Initialization:
t.left = 0, right = 0.formed = 0 (tracks satisfied unique characters).required (total unique characters in t).ans tuple/array to store (window_length, left, right) with default infinite length.Expansion Loop (Right Pointer):
right from 0 to len(s) - 1.c = s[right].c to current window counts.current_counts[c] matches target_counts[c], increment formed.Contraction Loop (Left Pointer):
formed == required.right - left + 1 is smaller than the current minimum, update ans.d = s[left].current_counts[d].d was a required character and current_counts[d] is now less than target_counts[d], decrement formed.left.Termination:
right reaches the end of s.ans. If ans still has infinite length, return "".The algorithm maintains the invariant that for any valid window ending at right, the inner while loop finds the largest possible left index such that s[left...right] is valid.
By testing every possible ending position (right from 0 to ) and, for each end position, minimizing the start position (left), we exhaustively search the solution space for the global minimum window without redundant checks. Since we never decrement left or right (they only move forward), we avoid infinite loops and redundant processing.
cpp#include <string> #include <vector> #include <climits> using namespace std; class Solution { public: string minWindow(string s, string t) { if (s.empty() || t.empty()) return ""; // Frequency map for target string t vector<int> map(128, 0); for (char c : t) { map[c]++; } int count = t.length(); // Total characters required int left = 0, right = 0; int minLen = INT_MAX; int head = 0; // Starting index of the min window while (right < s.length()) { // If char at right is in t, decrement count if (map[s[right]] > 0) { count--; } // Decrement map value for current char (indicates it's in window) map[s[right]]--; right++; // When window is valid (count == 0) while (count == 0) { // Update minimum window if (right - left < minLen) { minLen = right - left; head = left; } // Try to shrink from left // If the char at left was required (map value == 0), // removing it makes window invalid. // Note: We increment map[s[left]] first. If it becomes > 0, // it means it was a necessary char for t. if (map[s[left]] == 0) { count++; } map[s[left]]++; left++; } } return minLen == INT_MAX ? "" : s.substr(head, minLen); } };
javaclass Solution { public String minWindow(String s, String t) { if (s == null || t == null || s.length() == 0 || t.length() == 0) { return ""; } // Use integer array as hash map for ASCII int[] map = new int[128]; for (char c : t.toCharArray()) { map[c]++; } int left = 0; int right = 0; int minLen = Integer.MAX_VALUE; int startIndex = 0; int count = t.length(); // Characters to be found char[] sChars = s.toCharArray(); while (right < s.length()) { // Decrease count if the current char is one we need if (map[sChars[right]] > 0) { count--; } // Decrement frequency in map map[sChars[right]]--; right++; // While valid window while (count == 0) { if (right - left < minLen) { startIndex = left; minLen = right - left; } // Shrink window from left // If map[sChars[left]] is 0, it means we have exactly enough of this char. // Removing it will break the validity. if (map[sChars[left]] == 0) { count++; } map[sChars[left]]++; left++; } } return minLen == Integer.MAX_VALUE ? "" : s.substring(startIndex, startIndex + minLen); } }
pythonfrom collections import Counter class Solution: def minWindow(self, s: str, t: str) -> str: if not t or not s: return "" # Dictionary to keep a count of all the unique characters in t. dict_t = Counter(t) required = len(dict_t) # Filter all the characters from s into a new list along with their index. # The filtering criteria is that the character should be present in t. # (Optimization step optional, standard sliding window works directly on s) l, r = 0, 0 formed = 0 window_counts = {} # ans tuple of the form (window length, left, right) ans = float("inf"), None, None while r < len(s): # Add one character from the right to the window character = s[r] window_counts[character] = window_counts.get(character, 0) + 1 # If the frequency of the current character added equals to the # desired count in t then increment the formed count by 1. if character in dict_t and window_counts[character] == dict_t[character]: formed += 1 # Try and contract the window till the point where it ceases to be 'desirable'. while l <= r and formed == required: character = s[l] # Save the smallest window until now. if r - l + 1 < ans[0]: ans = (r - l + 1, l, r) # The character at the position pointed by the # `left` pointer is no longer a part of the window. window_counts[character] -= 1 if character in dict_t and window_counts[character] < dict_t[character]: formed -= 1 # Move the left pointer ahead, this would help to look for a new window. l += 1 # Keep expanding the window once we are done contracting. r += 1 return "" if ans[0] == float("inf") else s[ans[1] : ans[2] + 1]
Time Complexity:
t once to build the count map: .s using the sliding window. The right pointer moves from 0 to . The left pointer moves from 0 to . In the worst case, each character in s is visited twice (once by , once by ). This results in , which simplifies to .Space Complexity: or
The Sliding Window - Variable Size (Condition-Based) pattern is highly reusable. Understanding the expansion/contraction flow in LeetCode 76 allows you to solve several other problems:
Why does my solution get Time Limit Exceeded (TLE)?
count or formed variable.rightleftWhy is my output missing characters or including too many?
t. Using a Set instead of a Map/Frequency Array.t = "AA" and you use a Set, your logic will accept a window with a single "A", which is incorrect.Why does my code fail on edge cases like s="a", t="a"?
min_len incorrectly.left == right.Why is the logic for shrinking the window complex?
count variable based on incorrect conditions (e.g., checking if window_count < 0).count or formed variable should only change when a character's frequency in the window crosses the threshold defined by t.