Find All Anagrams in a String
Return all start indices of `p`'s anagrams in `s` — sliding window of letter counts.
6 min read
sliding-window hash-map strings
Problem#
Given strings s and p, return all start indices of substrings in s that are anagrams of p.
Examples#
s = "cbaebabacd", p = "abc"→[0,6].s = "abab", p = "ab"→[0,1,2].
Constraints#
1 <= s.length, p.length <= 3 * 10^4, lowercase letters.
Hints#
Hint
Slide a window of length
|p| over s. Use a counter difference; track how many letters are still “off”. Approach#
Maintain need[26] from p and a sliding window over s. Track a matches counter — how many letters in the window match need exactly. Slide by adding the right and removing the left; update matches incrementally. Record i - |p| + 1 whenever matches == 26.
Solution#
class Solution {public: vector<int> findAnagrams(string s, string p) { int n = s.size(), m = p.size(); vector<int> ans; if (n < m) return ans; int need[26] = {0}, have[26] = {0}; for (char c : p) ++need[c - 'a']; int matched = 0; for (int i = 0; i < 26; ++i) if (need[i] == 0) ++matched; for (int i = 0; i < n; ++i) { int add = s[i] - 'a'; ++have[add]; if (have[add] == need[add]) ++matched; else if (have[add] == need[add] + 1) --matched; if (i >= m) { int rem = s[i - m] - 'a'; --have[rem]; if (have[rem] == need[rem]) ++matched; else if (have[rem] == need[rem] - 1) --matched; } if (matched == 26) ans.push_back(i - m + 1); } return ans; }};func findAnagrams(s string, p string) []int { n, m := len(s), len(p) ans := []int{} if n < m { return ans } var need, have [26]int for i := 0; i < m; i++ { need[p[i]-'a']++ } matched := 0 for i := 0; i < 26; i++ { if need[i] == 0 { matched++ } } for i := 0; i < n; i++ { add := s[i] - 'a' have[add]++ if have[add] == need[add] { matched++ } else if have[add] == need[add]+1 { matched-- } if i >= m { rem := s[i-m] - 'a' have[rem]-- if have[rem] == need[rem] { matched++ } else if have[rem] == need[rem]-1 { matched-- } } if matched == 26 { ans = append(ans, i-m+1) } } return ans}from typing import List
class Solution: def findAnagrams(self, s: str, p: str) -> List[int]: n, m = len(s), len(p) ans: List[int] = [] if n < m: return ans need = [0] * 26 have = [0] * 26 for c in p: need[ord(c) - ord('a')] += 1 matched = sum(1 for i in range(26) if need[i] == 0) for i in range(n): add = ord(s[i]) - ord('a') have[add] += 1 if have[add] == need[add]: matched += 1 elif have[add] == need[add] + 1: matched -= 1 if i >= m: rem = ord(s[i - m]) - ord('a') have[rem] -= 1 if have[rem] == need[rem]: matched += 1 elif have[rem] == need[rem] - 1: matched -= 1 if matched == 26: ans.append(i - m + 1) return ansfunction findAnagrams(s, p) { const n = s.length, m = p.length; const ans = []; if (n < m) return ans; const need = new Array(26).fill(0); const have = new Array(26).fill(0); const A = 'a'.charCodeAt(0); for (let i = 0; i < m; i++) need[p.charCodeAt(i) - A]++; let matched = 0; for (let i = 0; i < 26; i++) if (need[i] === 0) matched++; for (let i = 0; i < n; i++) { const add = s.charCodeAt(i) - A; have[add]++; if (have[add] === need[add]) matched++; else if (have[add] === need[add] + 1) matched--; if (i >= m) { const rem = s.charCodeAt(i - m) - A; have[rem]--; if (have[rem] === need[rem]) matched++; else if (have[rem] === need[rem] - 1) matched--; } if (matched === 26) ans.push(i - m + 1); } return ans;}class Solution { public List<Integer> findAnagrams(String s, String p) { int n = s.length(), m = p.length(); List<Integer> ans = new ArrayList<>(); if (n < m) return ans; int[] need = new int[26]; int[] have = new int[26]; for (int i = 0; i < m; i++) need[p.charAt(i) - 'a']++; int matched = 0; for (int i = 0; i < 26; i++) if (need[i] == 0) matched++; for (int i = 0; i < n; i++) { int add = s.charAt(i) - 'a'; have[add]++; if (have[add] == need[add]) matched++; else if (have[add] == need[add] + 1) matched--; if (i >= m) { int rem = s.charAt(i - m) - 'a'; have[rem]--; if (have[rem] == need[rem]) matched++; else if (have[rem] == need[rem] - 1) matched--; } if (matched == 26) ans.add(i - m + 1); } return ans; }}function findAnagrams(s: string, p: string): number[] { const n = s.length, m = p.length; const ans: number[] = []; if (n < m) return ans; const need = new Array<number>(26).fill(0); const have = new Array<number>(26).fill(0); const A = 'a'.charCodeAt(0); for (let i = 0; i < m; i++) need[p.charCodeAt(i) - A]++; let matched = 0; for (let i = 0; i < 26; i++) if (need[i] === 0) matched++; for (let i = 0; i < n; i++) { const add = s.charCodeAt(i) - A; have[add]++; if (have[add] === need[add]) matched++; else if (have[add] === need[add] + 1) matched--; if (i >= m) { const rem = s.charCodeAt(i - m) - A; have[rem]--; if (have[rem] === need[rem]) matched++; else if (have[rem] === need[rem] - 1) matched--; } if (matched === 26) ans.push(i - m + 1); } return ans;}Editorial#
The matches counter avoids O(26) comparison per slide. We increment when a letter’s count first equals need, decrement when it exceeds need, and symmetrically on removal. Once matches == 26 (all 26 letters in correct count), the window is an anagram.
Complexity#
- Time: O(N).
- Space: O(1).
Concept revision#
Related#