Skip to content

Commit db44ff3

Browse files
chore: Merge pull request #148 from saulmaldonado/feat/minimum-window-substring
minimum window substring
2 parents eb81d49 + 6627b14 commit db44ff3

5 files changed

+419
-0
lines changed
+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Minimum Window Substring
2+
3+
## Difficulty
4+
5+
![Hard](https://img.shields.io/badge/hard-d9534f?style=for-the-badge&logoColor=white)
6+
7+
## Problem
8+
9+
Given two strings s and t, return the minimum window in s which will contain all the characters in t. If there is no such window in s that covers all characters in t, return the empty string "".
10+
11+
Note that If there is such a window, it is guaranteed that there will always be only one unique minimum window in s.
12+
13+
### Example 1
14+
15+
```
16+
Input: s = "ADOBECODEBANC", t = "ABC"
17+
Output: "BANC"
18+
```
19+
20+
### Example 2
21+
22+
```
23+
Input: s = "a", t = "a"
24+
Output: "a"
25+
```
26+
27+
### Constraints
28+
29+
`1 <= s.length, t.length <= 105`
30+
31+
`s and t consist of English letters.`
32+
33+
<details>
34+
<summary>Solutions (Click to expand)</summary>
35+
36+
### Explanation
37+
38+
#### Expanding and Contracting Window
39+
40+
Here a valid substrings indicates a substring of `s` that at the very least contains all of the characters of `t` (including duplicates) in no particular order. This means that if `s` is a valid substring then that means that `s` contains all of the character of `t`
41+
42+
```
43+
s = "ADOBECODEBANC", t = "ABC" // the entire string of s contains all of the characters of t
44+
^ ^ ^
45+
```
46+
47+
If we know that `s` is a valid substring then `s.substring(0, s.length)` is the largest possible answer. If we want to return the smallest possible substring then we would have to search the string using a **_sliding window_**. `left` and `right` would indicate the boundaries of the substring where `right - left + 1` is the length of the substring and `s.substring(left, right + 1)` is the current substring of the window.
48+
49+
If we want to ensure that all of the character of `t` are contained in `s.substring(left, right + 1)` then we would need a quick way to reference these characters. We could use a **_HashMap_** that represents the frequencies of the characters in `t`. This way validating a window with `t` would be as simple as checking that the frequencies of characters of `t` are included in the window.
50+
51+
```
52+
t = "ABC"
53+
{
54+
A: 1
55+
B: 1
56+
C: 1
57+
}
58+
59+
left = 0
60+
right = 5
61+
62+
s.substring(left, right + 1) = "ADOBEC" // VALID substring
63+
64+
{
65+
A: 1 <-
66+
D: 1
67+
O: 1
68+
B: 1 <-
69+
E: 1
70+
C: 1 <-
71+
}
72+
73+
// here the window substring contains all of the characters frequencies of t
74+
```
75+
76+
To check for valid substrings we would need to keep track of the window substring character frequencies.
77+
For this we would use a separate local hashmap.
78+
As we expand our window by incrementing `right` to include new characters, we would add the new character and update its frequency in the hashmap.
79+
As we contract our window by incrementing `left`, we would decrement the character frequency from our hashmap
80+
81+
```
82+
left = 0
83+
right = 5
84+
"ADOBECODEBANC"
85+
^ ^
86+
87+
{
88+
A: 1
89+
D: 1
90+
O: 1
91+
B: 1
92+
E: 1
93+
C: 1
94+
}
95+
96+
// expand our window by incrementing right
97+
98+
left = 0
99+
right = 6
100+
101+
"ADOBECODEBANC"
102+
^ ^
103+
{
104+
A: 1
105+
D: 1
106+
O: 2 <- O is incremented
107+
B: 1
108+
E: 1
109+
C: 1
110+
}
111+
112+
// contract our window by incrementing left
113+
114+
left = 1
115+
right = 6
116+
117+
"ADOBECODEBANC"
118+
^ ^
119+
{
120+
A: 0 <- A is decremented
121+
D: 1
122+
O: 2
123+
B: 1
124+
E: 1
125+
C: 1
126+
}
127+
```
128+
129+
As we find valid substring while expanding and contracting the window we would record the smallest length substring seen.
130+
131+
By the end of traversing to the end of `s` and we can't contract the window anymore, then we have finished searching the string
132+
133+
Time: `O(S + T)` Where `S` is the length of `s` and `T` is the length of `t`
134+
135+
Space: `O(S + T)` Where `S` is the length of the hashmap for our `s` window and `T` is the length of the hashmap for `t`
136+
137+
- [JavaScript](./minimum-window-substring.js)
138+
- [TypeScript](./minimum-window-substring.ts)
139+
- [Java](./minimum-window-substring.java)
140+
- [Go](./minimum-window-substring.go)
141+
142+
</details>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package minimumwindowsubstring
2+
3+
import "math"
4+
5+
func minWindow(s string, t string) string {
6+
n := len(s)
7+
8+
freq := map[byte]int{}
9+
10+
for i := range t {
11+
if _, ok := freq[t[i]]; !ok {
12+
freq[t[i]] = 0
13+
}
14+
freq[t[i]]++
15+
}
16+
17+
unique := len(freq)
18+
left := 0
19+
right := 0
20+
count := 0
21+
22+
minLeft := -1
23+
minRight := -1
24+
minLen := math.MaxInt32
25+
26+
window := map[byte]int{}
27+
28+
for right < n {
29+
cur := s[right]
30+
31+
if _, ok := freq[cur]; ok {
32+
if _, ok := window[cur]; !ok {
33+
window[cur] = 0
34+
}
35+
window[cur]++
36+
37+
if freq[cur] == window[cur] {
38+
count++
39+
}
40+
}
41+
42+
for left <= right && unique == count {
43+
l := right - left + 1
44+
45+
if minLeft == -1 || len < minLen {
46+
minLen = l
47+
minLeft = left
48+
minRight = right
49+
}
50+
51+
c := s[left]
52+
53+
if _, ok := freq[c]; ok {
54+
window[c]--
55+
56+
if window[c] < freq[c] {
57+
count--
58+
}
59+
}
60+
left++
61+
}
62+
right++
63+
}
64+
65+
if minLeft == -1 {
66+
return ""
67+
}
68+
69+
return s[minLeft : minRight+1]
70+
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
class Solution {
2+
public String minWindow(String s, String t) {
3+
int n = s.length();
4+
5+
Map<Character, Integer> freq = new HashMap<>();
6+
7+
for (char c : t.toCharArray()) {
8+
freq.put(c, freq.getOrDefault(c, 0) + 1);
9+
}
10+
11+
int unique = freq.size();
12+
13+
int left = 0;
14+
int right = 0;
15+
int count = 0;
16+
17+
int minLeft = -1;
18+
int minRight = -1;
19+
int minLen = Integer.MAX_VALUE;
20+
21+
Map<Character, Integer> window = new HashMap<>();
22+
23+
while (right < n) {
24+
char cur = s.charAt(right);
25+
26+
if (freq.containsKey(cur)) {
27+
window.put(cur, window.getOrDefault(cur, 0) + 1);
28+
29+
if (freq.get(cur).equals(window.get(cur))) {
30+
count++;
31+
}
32+
}
33+
34+
while (left <= right && unique == count) {
35+
int len = right - left + 1;
36+
37+
if (minLeft == -1 || len < minLen) {
38+
minLen = len;
39+
minLeft = left;
40+
minRight = right;
41+
}
42+
43+
char c = s.charAt(left);
44+
45+
if (freq.containsKey(c)) {
46+
window.put(c, window.get(c) - 1);
47+
48+
if (window.get(c) < freq.get(c)) {
49+
count--;
50+
}
51+
}
52+
left++;
53+
}
54+
right++;
55+
}
56+
57+
if (minLeft == -1) {
58+
return "";
59+
}
60+
61+
return s.substring(minLeft, minRight + 1);
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* @param {string} s
3+
* @param {string} t
4+
* @return {string}
5+
*/
6+
function minWindow(s, t) {
7+
const n = s.length;
8+
9+
const freq = {};
10+
11+
for (let i = 0; i < t.length; i++) {
12+
const c = t[i];
13+
if (freq[c] === undefined) {
14+
freq[c] = 0;
15+
}
16+
freq[c]++;
17+
}
18+
19+
const unique = Object.keys(freq).length;
20+
21+
let left = 0;
22+
let right = 0;
23+
let count = 0;
24+
25+
let minLeft = -1;
26+
let minRight = -1;
27+
let minLen = Number.MAX_SAFE_INTEGER;
28+
29+
const window = {};
30+
31+
while (right < n) {
32+
const cur = s[right];
33+
34+
if (freq[cur] !== undefined) {
35+
if (window[cur] === undefined) {
36+
window[cur] = 0;
37+
}
38+
39+
window[cur]++;
40+
41+
if (freq[cur] === window[cur]) {
42+
count++;
43+
}
44+
}
45+
46+
while (left <= right && unique === count) {
47+
let len = right - left + 1;
48+
49+
if (minLeft === -1 || len < minLen) {
50+
minLen = len;
51+
minLeft = left;
52+
minRight = right;
53+
}
54+
55+
const c = s[left];
56+
57+
if (freq[c] !== undefined) {
58+
window[c]--;
59+
60+
if (window[c] < freq[c]) {
61+
count--;
62+
}
63+
}
64+
left++;
65+
}
66+
right++;
67+
}
68+
69+
if (minLeft === -1) {
70+
return '';
71+
}
72+
73+
return s.substring(minLeft, minRight + 1);
74+
}

0 commit comments

Comments
 (0)