-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathminimumWindowSubstring.py
More file actions
288 lines (248 loc) · 11.8 KB
/
minimumWindowSubstring.py
File metadata and controls
288 lines (248 loc) · 11.8 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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#neetcode 15
class Solution(object):
def minWindow(self, s, t):
if t == "":
return ""
countT, window = {}, {}
# We count the string elements in the t string
for i in range(len(t)):
if t[i] in countT:
countT[t[i]] += 1
else:
countT[t[i]] = 1
# Next we have to initialise the variables for counting the elements in the s string
have, need = 0, len(countT)
res, reslen = [-1, -1], float('inf')
l = 0
# Now we will have to make the loop to count the elements in the s string from the right pointer
for r in range(len(s)):
c = s[r]
if c in window:
window[c] += 1
else:
window[c] = 1
# Now we have to check if we have what we need meaning if the elements in s match the elements in t
if c in countT and window[c] == countT[c]:
have += 1
# Now we check if they are equal or not and if the current window length
while have == need:
if (r - l + 1) < reslen:
res = [l, r]
reslen = r - l + 1
# Here we try to pop the left elements to check if we get shorter alternative answer
window[s[l]] -= 1
if s[l] in countT and window[s[l]] < countT[s[l]]:
have -= 1
l += 1
l, r = res
return s[l:r + 1]
if __name__ == "__main__":
sol = Solution()
s = "ADOBECODEBANC"
t = "ABC"
answer = sol.minWindow(s,t)
print(answer)
# Below is the full code again, followed by a very detailed, line-by-line explanation in simple, layman's language. I'll break down every part of the program and even give you a little visualization so you can see how it works.
# ---
# ```python
# class Solution:
# def minWindow(self, s: str, t: str) -> str:
# if t == "":
# return ""
#
# countT, window = {}, {}
#
# # Build a frequency dictionary for string t using an index-based loop
# for i in range(len(t)):
# if t[i] in countT:
# countT[t[i]] += 1
# else:
# countT[t[i]] = 1
#
# # 'have' counts how many unique characters in t we have enough of in the current window.
# # 'need' is the total number of unique characters we need (the size of countT).
# have, need = 0, len(countT)
#
# # 'res' will store the best window's start and end indices.
# # 'resLen' holds the length of that window; we start with infinity so any valid window is shorter.
# res, resLen = [-1, -1], float("infinity")
#
# # l is the left pointer of our sliding window, starting at index 0.
# l = 0
#
# # Now we start moving the right pointer (r) over the string s.
# for r in range(len(s)):
# c = s[r] # 'c' is the current character at the right pointer.
#
# # Add this character to our 'window' dictionary (which keeps track of counts within our current window).
# if c in window:
# window[c] += 1
# else:
# window[c] = 1
#
# # If the current character is one we care about (it's in t) and its count in our window
# # matches exactly the count needed (from countT), then we mark that we have one more fully satisfied character.
# if c in countT and window[c] == countT[c]:
# have += 1
#
# # Now, as long as our window contains all the characters with the required counts (have == need),
# # we try to shrink the window from the left to see if we can get a smaller valid window.
# while have == need:
# # If the current window is smaller than our previous best, update our result.
# if (r - l + 1) < resLen:
# res = [l, r]
# resLen = (r - l + 1)
#
# # Remove the leftmost character from the window since we're going to shrink it.
# window[s[l]] -= 1
# # If this removed character is part of t and now its count is less than needed,
# # it means our window is no longer valid, so we decrement 'have'.
# if s[l] in countT and window[s[l]] < countT[s[l]]:
# have -= 1
# l += 1 # Move the left pointer to the right.
#
# # After the loop, res holds the start and end indices of the smallest valid window.
# l, r = res
# # Return the substring of s from index l to r (inclusive). If no valid window was found, return an empty string.
# return s[l:r+1] if resLen != float("infinity") else ""
# ```
# ---
# ### Detailed Explanation in Layman's Terms
# 1. **Class & Function Declaration**
# - **Code:**
# ```python
# class Solution:
# def minWindow(self, s: str, t: str) -> str:
# ```
# - **Layman's Terms:**
# We define a container called `Solution` that holds our function. The function `minWindow` is what you call to find the smallest part (substring) of the main string `s` that contains every letter from string `t`.
# 2. **Early Return if t is Empty**
# - **Code:**
# ```python
# if t == "":
# return ""
# ```
# - **Layman's Terms:**
# If there is nothing to search for (if `t` is empty), then we immediately return an empty string because there is no work to do.
# 3. **Initialize Dictionaries for Counting**
# - **Code:**
# ```python
# countT, window = {}, {}
# ```
# - **Layman's Terms:**
# We create two empty "tally charts" (dictionaries):
# - `countT` will store how many times each character appears in `t`.
# - `window` will store the counts of characters in the current piece (window) of `s` we are looking at.
# 4. **Build the Frequency Dictionary for t**
# - **Code:**
# ```python
# for i in range(len(t)):
# if t[i] in countT:
# countT[t[i]] += 1
# else:
# countT[t[i]] = 1
# ```
# - **Layman's Terms:**
# We loop through each position (index) in string `t`. For each character at that position:
# - If the character is already in our tally (`countT`), add one to its count.
# - If it is not, start its count at 1.
#
# **Example:**
# If `t = "ABC"`, then after this loop, `countT` will be:
# `{'A': 1, 'B': 1, 'C': 1}`
# 5. **Set Up Counters for the Sliding Window**
# - **Code:**
# ```python
# have, need = 0, len(countT)
# res, resLen = [-1, -1], float("infinity")
# l = 0
# ```
# - **Layman's Terms:**
# - **`need`:** The number of unique characters we need to have (the number of keys in `countT`).
# - **`have`:** How many of those unique characters we currently have in our window with the right counts. Starts at 0.
# - **`res` & `resLen`:** These will store the best (smallest) window we find. We start with a dummy result `[-1, -1]` and set the best length to infinity so any real window will be smaller.
# - **`l`:** The left side of our sliding window. We start at the very beginning of `s` (index 0).
# 6. **Expand the Window by Moving the Right Pointer**
# - **Code:**
# ```python
# for r in range(len(s)):
# c = s[r]
# if c in window:
# window[c] += 1
# else:
# window[c] = 1
# ```
# - **Layman's Terms:**
# Now we slide a window over the main string `s` by moving the right pointer (`r`) from the beginning to the end:
# - For every character `c` at position `r`, we update our `window` tally.
# - If the character is already in `window`, increase its count.
# - If not, start its count at 1.
#
# **Visualization:**
# Imagine you have a magnifying glass (the window) that you slide along a long banner (`s`). As you slide it, you count the letters that come into view.
# 7. **Check if a Character Meets the Required Count**
# - **Code:**
# ```python
# if c in countT and window[c] == countT[c]:
# have += 1
# ```
# - **Layman's Terms:**
# - If the current character `c` is one we are interested in (it's in `t`), and the number of times it appears in our current window matches exactly what we need (from `countT`), then we say "Great, we now fully have this letter!" and increase our `have` count by 1.
# 8. **Try to Shrink the Window if All Requirements Are Met**
# - **Code:**
# ```python
# while have == need:
# if (r - l + 1) < resLen:
# res = [l, r]
# resLen = (r - l + 1)
# ```
# - **Layman's Terms:**
# - **`while have == need:`** means: "If our window currently has all the required letters in the right amounts…"
# - We then check: Is this window smaller than the best one we've seen so far?
# - **`(r - l + 1)`** calculates the size of the current window.
# - If it's smaller than our stored best (`resLen`), we update our result (`res`) with the current window's start (`l`) and end (`r`), and we record this new smaller length.
# 9. **Shrink the Window from the Left to See if We Can Still Keep It Valid**
# - **Code:**
# ```python
# window[s[l]] -= 1
# if s[l] in countT and window[s[l]] < countT[s[l]]:
# have -= 1
# l += 1
# ```
# - **Layman's Terms:**
# - Now that we have a valid window, we try to make it even smaller by removing the leftmost character.
# - We decrease the count of the character at the left pointer (`s[l]`).
# - If that character is one we need (it's in `countT`) and after removing it the count falls below what's required, we no longer fully have that character, so we reduce `have` by 1.
# - Finally, we move the left pointer one step to the right, effectively shrinking our window.
# 10. **Return the Final Result**
# - **Code:**
# ```python
# l, r = res
# return s[l:r+1] if resLen != float("infinity") else ""
# ```
# - **Layman's Terms:**
# - Once we've finished scanning the string, `res` holds the start and end indices of the smallest window that contains all required characters.
# - We then return the substring from `l` to `r` (making sure to include `r` by using `r+1`).
# - If no valid window was found (which would leave `resLen` as infinity), we return an empty string.
# ---
# ### Visualization Example
# Imagine:
# - **`s = "ADOBECODEBANC"`**
# - **`t = "ABC"`**
# 1. **Count Setup:**
# - `countT` becomes `{'A': 1, 'B': 1, 'C': 1}`.
# 2. **Sliding the Window:**
# - You start moving the right pointer. When the window first covers `"ADOBEC"`, it contains at least one A, one B, and one C.
# 3. **Shrinking the Window:**
# - Once you have all required letters, you try to shrink the window from the left (remove unnecessary characters) until the window is as small as possible while still containing A, B, and C.
# 4. **Final Answer:**
# - The smallest such window turns out to be `"BANC"`, which is returned by the function.
# ---
# ### Final Takeaway
# - **Purpose:**
# The code finds the smallest piece of the main string `s` that contains every character (with the correct count) from the string `t`.
# - **Key Ideas:**
# - **Dictionaries (`countT` & `window`):** They keep track of how many times each character appears.
# - **Sliding Window Technique:** We expand the window by moving the right pointer and try to shrink it from the left when possible.
# - **Tracking Validity:** Using `have` and `need`, we know when our window has met the requirements.
# This detailed, step-by-step explanation should make it really easy to understand how every part of the code works.