|
5 | 5 | - [Approach 1: Brute-force Checking](#approach-1-brute-force-checking) |
6 | 6 | - [Approach 2: Sorting and HashMap](#approach-2-sorting-and-hashmap) |
7 | 7 | - [Approach 3: Efficient Sliding Window with HashMap](#approach-3-efficient-sliding-window-with-hashmap) |
8 | | - |
| 8 | +- [Approach 4: Simplified Sliding Window with Counter](#approach-4-sliding-window-with-counter) |
9 | 9 | --- |
10 | 10 |
|
11 | 11 | ## Approach 1: Brute-force Checking |
@@ -129,3 +129,63 @@ findAnagrams("cbaebabacd", "abc") |
129 | 129 | ### Space Complexity |
130 | 130 | - O(1), as the space used by the Counter is constant relative to the fixed character set (assuming only lowercase letters). |
131 | 131 |
|
| 132 | +## Approach 4: Sliding Window with Counter |
| 133 | +### Intuition |
| 134 | + |
| 135 | +When looking for anagrams in a text, we need to compare character frequencies. Since we're looking for all possible positions, using a sliding window approach that maintains and updates character counts feels natural - this way we don't have to recount characters for each possible position. |
| 136 | + |
| 137 | +### Approach |
| 138 | + |
| 139 | +1. First validate the inputs - if either string is empty or the text is shorter than pattern, return empty list |
| 140 | +2. Create a frequency counter for the pattern we're looking for |
| 141 | +3. Create initial window of pattern length at start of text and count its characters |
| 142 | +4. Compare first window with pattern - if they match, we found our first anagram |
| 143 | +5. For rest of text, slide window one character at a time: |
| 144 | + - Add new character on right to window count |
| 145 | + - Remove leftmost character from window count |
| 146 | + - If character count becomes zero, remove it completely |
| 147 | + - Compare current window with pattern |
| 148 | + - If frequencies match, add starting position to results |
| 149 | +6. Return all positions where anagrams were found |
| 150 | + |
| 151 | +### Code |
| 152 | +```python |
| 153 | +from collections import Counter |
| 154 | + |
| 155 | +def find_anagrams(text: str, pattern: str) -> list: |
| 156 | + """Find starting indices of pattern's anagrams in text.""" |
| 157 | + if not text or not pattern or len(text) < len(pattern): |
| 158 | + return [] |
| 159 | + |
| 160 | + result = [] |
| 161 | + pattern_count = Counter(pattern) |
| 162 | + window_count = Counter(text[:len(pattern)]) |
| 163 | + |
| 164 | + # Check first window |
| 165 | + if window_count == pattern_count: |
| 166 | + result.append(0) |
| 167 | + |
| 168 | + # Slide window: remove left char, add right char, check if anagram |
| 169 | + for i in range(len(pattern), len(text)): |
| 170 | + window_count[text[i]] += 1 |
| 171 | + window_count[text[i - len(pattern)]] -= 1 |
| 172 | + |
| 173 | + # Clean up zero counts |
| 174 | + if window_count[text[i - len(pattern)]] == 0: |
| 175 | + del window_count[text[i - len(pattern)]] |
| 176 | + |
| 177 | + if window_count == pattern_count: |
| 178 | + result.append(i - len(pattern) + 1) |
| 179 | + |
| 180 | + return result |
| 181 | +``` |
| 182 | + |
| 183 | +### Complexity |
| 184 | + |
| 185 | +- Time complexity: $$O(n)$$ where n is length of input text |
| 186 | + - We scan text once with constant time operations at each step |
| 187 | + - Counter comparisons are O(k) where k is alphabet size (constant) |
| 188 | +- Space complexity: $$O(k)$$ where k is size of alphabet |
| 189 | + - We store two frequency counters |
| 190 | + - Each counter has at most k entries (one per unique character) |
| 191 | + - k is limited by alphabet size, so effectively O(1) |
0 commit comments