Files
troupe-standup/wizard-work/beam/min_window_substring.py
T

80 lines
2.6 KiB
Python

"""Minimum window substring (LeetCode 76).
Given strings s and t, return the smallest substring of s such that every
character in t (with multiplicity) occurs in it. Empty string if none.
Algorithm: sliding window with two pointers and a "need" counter.
Invariant: window [left, right) is the smallest window ending at right-1 that
contains t, tracked via `formed` count of satisfied character requirements.
We expand right until all requirements met, then shrink left as far as possible
while still meeting them, recording the best.
"""
from collections import Counter
def min_window(s: str, t: str) -> str:
if not t or not s or len(t) > len(s):
return ""
need = Counter(t)
required = len(need) # distinct chars needed
window = {}
formed = 0
left = 0
best_len = float("inf")
best_start = 0
for right, ch in enumerate(s):
window[ch] = window.get(ch, 0) + 1
if ch in need and window[ch] == need[ch]:
formed += 1
while left <= right and formed == required:
cur_len = right - left + 1
if cur_len < best_len:
best_len = cur_len
best_start = left
lc = s[left]
window[lc] -= 1
if lc in need and window[lc] < need[lc]:
formed -= 1
left += 1
if best_len == float("inf"):
return ""
return s[best_start:best_start + best_len]
def _brute(s, t):
from collections import Counter
nt = Counter(t)
best = None
n = len(s)
for i in range(n):
for j in range(i + len(t), n + 1):
w = s[i:j]
if all(Counter(w)[c] >= nt[c] for c in nt):
if best is None or len(w) < len(best):
best = w
break
return best or ""
if __name__ == "__main__":
import random, string
# property-based randomized check against brute force
rng = random.Random(42)
for _ in range(2000):
n = rng.randint(0, 12)
s = "".join(rng.choice("abcABC") for _ in range(n))
m = rng.randint(0, 6)
t = "".join(rng.choice("abcABC") for _ in range(m))
a = min_window(s, t)
b = _brute(s, t)
# compare lengths (ties: any minimal window acceptable, but brute returns first;
# we only assert length equality and validity)
assert len(a) == len(b), (s, t, a, b)
# validity
if a:
from collections import Counter
cnt = Counter(a)
nt = Counter(t)
assert all(cnt[c] >= nt[c] for c in nt)
print("property tests passed")