1+ """
2+ LeetCode 76. Minimum Window Substring
3+ =====================================
4+
5+ Problem Statement
6+ -----------------
7+ Given two strings `s` and `t`, return the minimum window substring
8+ of `s` such that every character in `t` (including duplicates)
9+ is included in the window.
10+
11+ If no such substring exists, return an empty string "".
12+
13+ Examples
14+ --------
15+ Input:
16+ s = "ADOBECODEBANC"
17+ t = "ABC"
18+
19+ Output:
20+ "BANC"
21+
22+ Input:
23+ s = "a"
24+ t = "a"
25+
26+ Output:
27+ "a"
28+
29+ Input:
30+ s = "a"
31+ t = "aa"
32+
33+ Output:
34+ ""
35+
36+ Approach
37+ --------
38+ Sliding Window + Frequency Counter
39+
40+ 1. Store character frequencies of string `t` using Counter.
41+ 2. Expand the right pointer and decrease the required count.
42+ 3. Once all characters are found (count == 0):
43+ - Try shrinking the window from the left.
44+ - Update the minimum window if a smaller valid window is found.
45+ 4. Continue until the entire string is processed.
46+
47+ Key Observation
48+ ---------------
49+ Counter values represent how many more occurrences of each character
50+ are still required.
51+
52+ Positive Value -> Character still needed.
53+ Zero Value -> Exact requirement satisfied.
54+ Negative Value -> Extra occurrences present in the current window.
55+
56+ Time Complexity
57+ ---------------
58+ O(n)
59+
60+ Each character is visited at most twice:
61+ - Once by the right pointer.
62+ - Once by the left pointer.
63+
64+ Space Complexity
65+ ----------------
66+ O(m)
67+
68+ where m = number of unique characters in t.
69+
70+ """
71+
72+
73+ from collections import Counter
74+
75+
76+ class Solution :
77+ def minWindow (self , s : str , t : str ) -> str :
78+
79+ # Edge Case
80+ if len (s ) < len (t ):
81+ return ""
82+
83+ # Frequency map of characters needed
84+ n = Counter (t )
85+
86+ # Left pointer
87+ l = 0
88+
89+ # Total characters still needed
90+ count = len (t )
91+
92+ # Minimum window length
93+ ans = float ('inf' )
94+
95+ # Starting index of answer
96+ start = 0
97+
98+ # Expand window using right pointer
99+ for i in range (len (s )):
100+
101+ if s [i ] in n :
102+ n [s [i ]] -= 1
103+
104+ # Required character found
105+ if n [s [i ]] >= 0 :
106+ count -= 1
107+
108+ # Valid window found
109+ while count == 0 :
110+
111+ # Update minimum window
112+ if i - l + 1 < ans :
113+ ans = i - l + 1
114+ start = l
115+
116+ # Remove left character
117+ if s [l ] in n :
118+ n [s [l ]] += 1
119+
120+ # Window becomes invalid
121+ if n [s [l ]] > 0 :
122+ count += 1
123+
124+ l += 1
125+
126+ if ans == float ('inf' ):
127+ return ""
128+
129+ return s [start :start + ans ]
130+
131+
132+ # ============================================================
133+ # Dry Run
134+ # ============================================================
135+
136+ """
137+ Input:
138+ s = "ADOBECODEBANC"
139+ t = "ABC"
140+
141+ Initial:
142+ n = {'A':1, 'B':1, 'C':1}
143+ count = 3
144+ l = 0
145+
146+ ------------------------------------------------------------
147+ i = 0 -> 'A'
148+
149+ n['A'] = 0
150+ count = 2
151+
152+ Window = "A"
153+
154+ ------------------------------------------------------------
155+ i = 3 -> 'B'
156+
157+ n['B'] = 0
158+ count = 1
159+
160+ Window = "ADOB"
161+
162+ ------------------------------------------------------------
163+ i = 5 -> 'C'
164+
165+ n['C'] = 0
166+ count = 0
167+
168+ Window = "ADOBEC"
169+
170+ Valid Window Found
171+
172+ Length = 6
173+ ans = 6
174+ start = 0
175+
176+ Try Shrinking
177+
178+ Remove 'A'
179+ n['A'] = 1
180+
181+ n['A'] > 0
182+ count = 1
183+
184+ Stop Shrinking
185+
186+ ------------------------------------------------------------
187+ Continue Expanding
188+
189+ i = 10 -> 'A'
190+
191+ n['A'] = 0
192+ count = 0
193+
194+ Window Valid Again
195+
196+ Try Shrinking
197+
198+ Remove D
199+ Remove O
200+ Remove B
201+ Remove E
202+
203+ Window = "CODEBA"
204+
205+ Remove C
206+
207+ n['C'] = 1
208+ count = 1
209+
210+ Stop
211+
212+ ------------------------------------------------------------
213+ i = 12 -> 'C'
214+
215+ n['C'] = 0
216+ count = 0
217+
218+ Window = "BANC"
219+
220+ Length = 4
221+
222+ ans = 4
223+ start = 9
224+
225+ Try Shrinking
226+
227+ Remove B
228+
229+ n['B'] = 1
230+ count = 1
231+
232+ Stop
233+
234+ ------------------------------------------------------------
235+
236+ Answer:
237+ s[9:13]
238+
239+ = "BANC"
240+
241+ Output:
242+ "BANC"
243+ """
244+
245+ # ============================================================
246+ # Example Usage
247+ # ============================================================
248+
249+ if __name__ == "__main__" :
250+ solution = Solution ()
251+
252+ print (solution .minWindow ("ADOBECODEBANC" , "ABC" ))
253+ print (solution .minWindow ("a" , "a" ))
254+ print (solution .minWindow ("a" , "aa" ))
255+
256+
257+ """
258+ Interview Explanation
259+ ---------------------
260+
261+ Why do we decrement n[s[i]]?
262+
263+ Because the character has entered the current window.
264+
265+ Example:
266+ Need:
267+ A : 1
268+
269+ After finding one A:
270+ A : 0
271+
272+ Requirement satisfied.
273+
274+ ------------------------------------------------
275+
276+ Why check n[s[i]] >= 0 ?
277+
278+ Because only required occurrences should reduce count.
279+
280+ Example:
281+
282+ Need:
283+ A : 1
284+
285+ Window:
286+ A A A
287+
288+ Counter values:
289+
290+ 0
291+ -1
292+ -2
293+
294+ Only the first A contributes toward satisfying t.
295+
296+ ------------------------------------------------
297+
298+ Why increment n[s[l]] while shrinking?
299+
300+ Because that character leaves the window.
301+
302+ If its count becomes positive,
303+ the window no longer contains enough copies of that character.
304+
305+ ------------------------------------------------
306+
307+ Why does this work in O(n)?
308+
309+ Each character:
310+ - enters the window once
311+ - leaves the window once
312+
313+ Hence total operations are linear.
314+
315+ O(n)
316+ """
0 commit comments