How do you find the minimum window substring that contains every character of a target string?
Count how many times each character is needed, then slide a window over the source while tracking how many of those requirements are currently satisfied. Extend the window to the right. Whenever every requirement is satisfied, start shrinking from the left, recording the window length each time it improves, and keep shrinking until the window stops satisfying the requirements.
from collections import Counter
def min_window(s, t):
if not s or not t or len(t) > len(s):
return ""
need = Counter(t)
window = Counter()
left = 0
formed = 0
best = (float("inf"), 0, 0)
for right, char in enumerate(s):
window[char] += 1
if char in need and window[char] == need[char]:
formed += 1
while formed == len(need):
if right - left + 1 < best[0]:
best = (right - left + 1, left, right)
outgoing = s[left]
window[outgoing] -= 1
if outgoing in need and window[outgoing] < need[outgoing]:
formed -= 1
left += 1
return "" if best[0] == float("inf") else s[best[1]:best[2] + 1]
print(min_window("ADOBECODEBANC", "ABC"))
print(min_window("a", "aa"))The outputs are BANC and an empty string. The window from index nine to twelve contains the A, the B and the C, and no shorter run does. The second call asks for two a characters from a source that has only one, so no window can ever satisfy the requirement and the function returns an empty string.
The mechanism is the formed counter. It counts how many distinct required characters are currently present in sufficient quantity, and it only moves when a count reaches exactly the required amount or falls below it. Counting distinct requirements rather than individual characters is what makes repeated letters in the target work correctly: for a target of aab you need two a characters and one b, and the counter tracks those three requirements separately.
The trade-off to name is that the algorithm is a sliding window with two pointers and two counters, so the space is O of the alphabet size and the time is O of the sum of the two string lengths. Each pointer moves in one direction only, which is what keeps it linear. The alternative, generating every substring and testing it, is cubic in the worst case and is the mistake this problem is designed to expose.