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. For example:
Could you provide an efficient algorithm to solve this problem, including an analysis of its time and space complexity? Also, consider various edge cases such as empty strings or when t
requires more characters than s
contains.
## Minimum Window Substring
This problem requires finding the smallest substring within a larger string `s` that contains all characters from another string `t`, including duplicates. If no such substring exists, we return an empty string.
### 1. Naive (Brute Force) Solution
One straightforward approach is to generate all possible substrings of `s` and then check each substring to see if it contains all the characters of `t`. This approach is inefficient but helps in understanding the problem.
```python
def min_window_brute_force(s: str, t: str) -> str:
if not s or not t:
return ""
min_len = float('inf')
min_window = ""
for i in range(len(s)):
for j in range(i, len(s)):
substring = s[i:j+1]
if all(char in substring and substring.count(char) >= t.count(char) for char in set(t)):
if len(substring) < min_len:
min_len = len(substring)
min_window = substring
return min_window
A more efficient approach uses the sliding window technique. We maintain a window in s
and expand it until it contains all characters from t
. Then, we contract the window to find the minimum window size.
from collections import defaultdict
def min_window(s: str, t: str) -> str:
if not s or not t:
return ""
# Create a dictionary to store the frequency of characters in t
dict_t = defaultdict(int)
for char in t:
dict_t[char] += 1
required = len(dict_t)
# left and right pointers, formed counter and window counts
left, right = 0, 0
formed = 0
window_counts = defaultdict(int)
ans = float('inf'), None, None # (window length, left, right)
while right < len(s):
character = s[right]
window_counts[character] += 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 left <= right and formed == required:
character = s[left]
# Save the smallest window until now.
if right - left + 1 < ans[0]:
ans = (right - left + 1, left, right)
# 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.
left += 1
# Keep expanding the window once we are done contracting.
right += 1
return "" if ans[0] == float('inf') else s[ans[1]: ans[2] + 1]
s
. Generating all substrings takes O(m^2) time, and checking each substring takes O(m) time, summing up to O(m^3).s
and n is the length of string t
. We iterate through s
at most twice (once with the right pointer and once with the left pointer), and the operations on the character frequency dictionaries take constant time because the size of the English alphabet is fixed.s
, due to storing the substrings.t
, which is bounded by the size of the alphabet. Thus, it is constant space.s
or t
is empty, the function should return an empty string.t
Larger than s
: If t
contains characters that are not in s
or has a higher frequency of characters than s
, the function should return an empty string.t
has duplicate characters not present in s
: If s
does not have the required count of duplicate characters present in t
, the function should return an empty string.Example Usage:
s = "ADOBECODEBANC"
t = "ABC"
print(min_window(s, t)) # Output: "BANC"
s = "a"
t = "a"
print(min_window(s, t)) # Output: "a"
s = "a"
t = "aa"
print(min_window(s, t)) # Output: ""