|
1 | 1 | # 76. Minimum Window Substring
|
2 | 2 |
|
3 | 3 | ## Two Pointer with Map Solution
|
4 |
| -- Runtime: O(S * T) but O(S * (S+T)) due to string slicing |
| 4 | +- Runtime: O(S * T) + O(T) but O(S * (S+T)) + O(T) due to string slicing |
5 | 5 | - Space: O(S)
|
6 | 6 | - S = Number of characters in string S
|
7 |
| -- T = Number of unique characters in string T |
| 7 | +- T = Number of characters in string T |
8 | 8 |
|
9 | 9 | So the definition of a result requires the substring to contain all characters in T. It then wants the smallest substring.
|
10 | 10 | With that, we have to begin the solution by finding the first instance of that substring.
|
@@ -65,10 +65,10 @@ def chars_occur_ge(ch_to_n_counts, all_ch_counts):
|
65 | 65 | ```
|
66 | 66 |
|
67 | 67 | ## Two Pointer with Map Solution (Optimized)
|
68 |
| -- Runtime: O(S) |
| 68 | +- Runtime: O(S) + O(T) but O(S * S) + O(T) due to string slicing |
69 | 69 | - Space: O(S)
|
70 | 70 | - S = Number of characters in string S
|
71 |
| -- T = Number of unique characters in string T |
| 71 | +- T = Number of characters in string T |
72 | 72 |
|
73 | 73 | We can further improve the solution by optimizing the way we check if its a valid substring.
|
74 | 74 | We can still use a dictionary to count the occurances, but we can also keep a separate count for the unique characters in T.
|
@@ -127,3 +127,52 @@ class CharacterCounter:
|
127 | 127 | def is_valid(self):
|
128 | 128 | return self._n_valid_chars == len(self._source_counts.keys())
|
129 | 129 | ```
|
| 130 | + |
| 131 | +## Two Pointer with Map Solution (No String slicing) |
| 132 | +- Runtime: O(S) + O(T) |
| 133 | +- Space: O(S) |
| 134 | +- S = Number of characters in string S |
| 135 | +- T = Number of characters in string T |
| 136 | + |
| 137 | +``` |
| 138 | +from collections import defaultdict |
| 139 | +from collections import Counter |
| 140 | +
|
| 141 | +class Solution: |
| 142 | + def minWindow(self, s: str, t: str) -> str: |
| 143 | + char_counter = CharacterCounter(t) |
| 144 | + left_i, left_i_result, right_i_result = 0, 0, len(s)-1 |
| 145 | + found = False |
| 146 | + for right_i, right_ch in enumerate(s): |
| 147 | + char_counter += right_ch |
| 148 | + if char_counter.is_valid: |
| 149 | + while left_i <= right_i and char_counter.is_valid: |
| 150 | + found = True |
| 151 | + if right_i-left_i < right_i_result-left_i_result: |
| 152 | + right_i_result, left_i_result = right_i, left_i |
| 153 | + char_counter -= s[left_i] |
| 154 | + left_i += 1 |
| 155 | + return s[left_i_result:right_i_result+1] if found else '' |
| 156 | + |
| 157 | +class CharacterCounter: |
| 158 | + def __init__(self, source_str): |
| 159 | + self._ch_to_n_counts = defaultdict(int) |
| 160 | + self._source_counts = Counter(source_str) |
| 161 | + self._n_valid_chars = 0 |
| 162 | +
|
| 163 | + def __iadd__(self, char): |
| 164 | + self._ch_to_n_counts[char] += 1 |
| 165 | + if char in self._source_counts and self._ch_to_n_counts[char] == self._source_counts[char]: |
| 166 | + self._n_valid_chars += 1 |
| 167 | + return self |
| 168 | + |
| 169 | + def __isub__(self, char): |
| 170 | + self._ch_to_n_counts[char] -= 1 |
| 171 | + if char in self._source_counts and self._ch_to_n_counts[char] == self._source_counts[char]-1: |
| 172 | + self._n_valid_chars -= 1 |
| 173 | + return self |
| 174 | + |
| 175 | + @property |
| 176 | + def is_valid(self): |
| 177 | + return self._n_valid_chars == len(self._source_counts.keys()) |
| 178 | +``` |
0 commit comments