Repository navigation
Expand file tree
/
Copy pathminimum_window_substring.py
More file actions
64 lines (49 loc) · 2.49 KB
/
Copy pathminimum_window_substring.py
File metadata and controls
64 lines (49 loc) · 2.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
from collections import Counter, defaultdict
class Solution:
"""LeetCode #76 - Minimum Window Substring (Hard)
https://leetcode.com/problems/minimum-window-substring/
Return the shortest substring of `s` that contains every character of `t`,
including duplicates. Order does not matter. If no such window exists,
return "".
"""
def minWindow(self, s: str, t: str) -> str:
"""Sliding window that grows on the right and shrinks on the left.
The window is only ever *valid* or *invalid*, so the loop has a simple
shape: extend right until valid, then shrink left as far as validity
survives, recording the best length each time.
The trick that keeps it O(n) is the `satisfied` counter. Comparing the
whole window map against `need` on every step would cost O(alphabet)
per character; instead we track how many *distinct* characters have
reached their required count, and update that counter only at the exact
moment a character crosses its threshold:
window[ch] == need[ch] -> one more character satisfied
window[ch] < need[ch] -> one fewer (only right after removal)
Note the strict `==` / `<`. Using `>=` would re-count a character every
time an extra copy arrives, which silently breaks the counter.
Time: O(len(s) + len(t)) - each index enters and leaves the window once.
Space: O(len(t)) for the two maps (bounded by the alphabet).
"""
if not s or not t or len(t) > len(s):
return ""
need = Counter(t)
required = len(need) # distinct characters we must cover
window = defaultdict(int)
satisfied = 0
best_len = float("inf")
best_start = 0
left = 0
for right, ch in enumerate(s):
window[ch] += 1
if ch in need and window[ch] == need[ch]:
satisfied += 1
# Window is valid - shrink it while it stays valid.
while satisfied == required:
if right - left + 1 < best_len:
best_len = right - left + 1
best_start = left
dropped = s[left]
window[dropped] -= 1
if dropped in need and window[dropped] < need[dropped]:
satisfied -= 1 # this removal is what broke validity
left += 1
return "" if best_len == float("inf") else s[best_start:best_start + best_len]