Browse curriculum

Find All Anagrams in a String

Find every start index of an anagram of p in s with a fixed-size sliding window that keeps letter counts up to date as it moves.

Problem Understanding

Find All Anagrams in a String: given strings s and p, return every start index in s where a substring is an anagram of p (the same letters with the same counts, in any order).

Example: s = "cbaebabacd", p = "abc" returns [0, 6]: "cba" and "bac".

The Intuition: A Window That Keeps Its Counts

Every candidate is a substring of length |p|, so use a fixed-size sliding window. Two strings are anagrams exactly when their letter counts match, the same test as Valid Anagram.

Recounting each window would be wasteful. Instead, keep the window's counts current:

  1. The letter entering on the right: +1.
  2. The letter leaving on the left: −1.
  3. If the counts equal p's, record the start index.

Permutation in String uses the same window but only asks whether one match exists.

Interactive Walkthrough

The window slides across s, with its newest letter filled. It turns green whenever its counts match p, and that start index is recorded. Try Repeats to see why counts matter, not just which letters appear.

Dry Run Table

Input: s = "cbaebabacd", p = "abc"

Window Start Matches p?
cba 0 ✓
bae 1 ✗
aeb 2 ✗
eba 3 ✗
bab 4 ✗
aba 5 ✗
bac 6 ✓
acd 7 ✗

Answer: [0, 6].

The Solution Template

Two 26-slot count arrays and a window of fixed length.

Find All Anagrams in a String Code
1var findAnagrams = function(s, p) {
2 const m = p.length, need = new Array(26).fill(0), have = new Array(26).fill(0), ans = [];
3 for (const c of p) need[c.charCodeAt(0) - 97]++;
4 for (let i = 0; i < s.length; i++) {
5 have[s.charCodeAt(i) - 97]++;
6 if (i >= m) have[s.charCodeAt(i - m) - 97]--;
7 if (have.every((v, k) => v === need[k])) ans.push(i - m + 1);
8 }
9 return ans;
10};

Edge Cases & Common Mistakes

  • p longer than s: no window ever reaches length |p|; the answer is [].
  • Overlapping matches: s = "abab", p = "ab" gives [0, 1, 2]; keep sliding after a match.
  • Comparing letter sets instead of counts: "aab" and "abb" share letters but are not anagrams.
  • Off-by-one on the left edge: the letter that leaves is s[i - |p|], and the window starts at i - |p| + 1.
  • Faster comparison: track how many of the 26 letters currently match, so each move updates a counter instead of comparing arrays.

Summary

Count p once, then slide a window of length |p| over s, adding the entering letter and removing the leaving one. Whenever the two count arrays are equal, record the window's start. O(n) time and O(1) space for a 26-letter alphabet.

More practice: DSA interview questions with answers · patterns cheat sheet

The approach, step by step

  1. Count p

    Build a 26-slot count of p's letters.

  2. Slide a window of length |p|

    For each index i in s, add s[i] to the window counts; once i ≥ |p|, subtract s[i − |p|].

  3. Compare and record

    If the window counts equal p's counts, append i − |p| + 1 to the answer.

Frequently asked questions

How does the sliding window find all anagrams?

An anagram of p has exactly p's letter counts. Keep a window of length |p| over s and update its counts in O(1) per move: add the letter entering on the right, subtract the one leaving on the left. Whenever the window's counts equal p's, record the window's start index.


Why not sort every substring of length |p|?

Sorting each window costs O(m log m), for O(n · m log m) overall. Updating counts as the window slides costs O(1) per step plus a 26-entry comparison, so the whole scan is O(n).


How is this different from Permutation in String?

It is the same window. Permutation in String asks whether any window matches and can stop at the first one; Find All Anagrams collects every matching start index.

Applying for software engineering roles? Free, no sign-up:

AI resume checker — match your resume to the job description