Find All Anagrams in a String
Problem
Given two strings s and p, return an array of all the start indices of p's anagrams in s. You may return the answer in any order.
- 1 ≤ s.length, p.length ≤ 3 * 10⁴
- s and p consist of lowercase English letters.
Example
s = "cbaebabacd", p = "abc"[0, 6]The substring starting at index 0 is "cba", which is an anagram of "abc". The substring starting at index 6 is "bac", which is also an anagram of "abc". The algorithm uses a sliding window of size equal to the length of p, maintaining a frequency count of characters in the current window and comparing it to the frequency count of p. When the counts match, the start index of the window is recorded.
Approach
Straightforward Solution
A brute-force approach would check every substring of s of length p, count characters, and compare to p's count. This results in O(n*k) time complexity, where n is the length of s and k is the length of p, which is inefficient for large inputs.
Core Observation
An anagram requires the frequency of each character in the substring to match exactly the frequency in p. This means the problem reduces to finding all substrings of s of length equal to p where the character counts match.
Path to Optimal
PreviewThe key recognition signals are 'find all substrings of fixed length with matching character counts' and 'anagrams'. These indicate the Sliding Window pattern because the problem requires checking contiguous substrings of fixed size and comparing frequency counts efficiently…
Full step-by-step walkthrough on Pro →
Optimal Approach
PreviewUse a sliding window of size k = len(p) over s. Maintain two frequency counters: one for p (need) and one for the current window (window)…
Full step-by-step walkthrough on Pro →
Want the full reasoning chain?
Unlock the complete walkthrough, line-by-line analysis, and recall drill.
Unlock ProTime
O(n)
Each character in s is visited once when added to the window and once when removed, resulting in a linear pass. Frequency comparisons are O(1) on average due to fixed alphabet size and efficient Counter equality checks.
Space
O(1)
Frequency counters store counts for lowercase English letters only, a fixed size of 26, which is constant auxiliary space independent of input size.
Pattern Spotlight
Sliding Window (Fixed-Size Frequency Matching)
When searching for all substrings of fixed length that satisfy a frequency-based condition, maintain a sliding window with incremental updates to frequency counts and compare to the target frequency map to detect matches efficiently.
Solution
| 1 | class Solution: |
| 2 | def findAnagrams(self, s: str, p: str) -> list[int]: |
| 3 | need = Counter(p) |
| 4 | window = Counter() |
| 5 | res = [] |
| 6 | k = len(p) |
| 7 | |
| 8 | for right, ch in enumerate(s): |
| 9 | window[ch] += 1 |
| 10 | |
| 11 | if right >= k: |
| 12 | left_ch = s[right - k] |
| 13 | window[left_ch] -= 1 |
| 14 | |
| 15 | if window[left_ch] == 0: |
| 16 | del window[left_ch] |
| 17 | |
| 18 | if window == need: |
| 19 | res.append(right - k + 1) |
| 20 | |
| 21 | return res |
Step-by-Step Solution
Initialize Frequency Counters and Result List
| 3 | need = Counter(p) |
| 4 | window = Counter() |
| 5 | res = [] |
| 6 | k = len(p) |
Objective
To set up the frequency map for the target string p and prepare the sliding window frequency map and result container.
Key Insight
Precomputing the frequency of characters in p (need) allows constant-time comparison with the sliding window frequency map. Initializing an empty window counter and result list prepares the algorithm to track the current window's character counts and store valid start indices efficiently.
Interview Quick-Check
Core Logic
Using Counter for both need and window enables O(1) average-time frequency comparisons due to fixed alphabet size.
State & Boundaries
Setting k = len(p) defines the fixed window size, which is critical for sliding window correctness.
Common Pitfalls & Bugs
Forgetting to initialize the window counter or result list leads to runtime errors or incorrect results.
Expand Sliding Window and Maintain Frequency Counts
To iterate over s, expanding the window by adding the right character and shrinking it by removing the left character when the window exceeds size k.
Detect Anagram Matches and Record Start Indices
To compare the current window's frequency counter with the target frequency counter and record the start index when they match.
Return the List of Anagram Start Indices
To return the accumulated list of starting indices of all anagrams found in s.
3 more steps with full analysis available on Pro.
Line Analysis
This solution has 2 Critical lines interviewers watch for.
if window == need:
Check if the current window's frequency matches the target frequency.
This line embodies the algorithm's essence: comparing frequency counters directly detects anagrams in O(1) time on average, enabling efficient identification of valid substrings.
if window[left_ch] == 0:
Delete the character from the window counter if its count reaches zero.
Deleting zero-count keys is critical because Counter equality checks consider keys; leaving zero-count keys would cause false mismatches, breaking the anagram detection.
Full line-by-line criticality + rationale for all 14 lines available on Pro.
Test Your Understanding
Why does comparing the frequency counters of the current window and the target string p guarantee that the substring is an anagram?
See the answer with Pro.
Related Problems
Sliding Window pattern
Don't just read it. Drill it.
Reconstruct Find All Anagrams in a String from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.
Unlock the Find All Anagrams in a String drill