LeetCode ยท #438

Find All Anagrams in a String Solution

Given strings s and p, return an array of all start indices of p's anagrams in s.

01
Section One ยท Problem

Problem Statement

๐Ÿท๏ธ

Difficulty

Medium

๐Ÿ”—

LeetCode

Problem #438

๐Ÿ—๏ธ

Pattern

Sliding Window โ€” fixed size + frequency

Given two strings s and p, return an array of all the start indices of p's anagrams in s. An anagram is a rearrangement โ€” same characters, same counts, different order. You may return the answer in any order.

Example
Input: s = "cbaebabacd", p = "abc" Output: [0, 6] // "cba" at index 0 and "bac" at index 6 are anagrams of "abc"
Constraints
โ€ข 1 โ‰ค s.length, p.length โ‰ค 3 ร— 10โด โ€ข s and p consist of lowercase English letters
02
Section Two ยท Approach 1

Brute Force โ€” Sort Each Window

For every window of size len(p) in s, sort the window and compare with sorted p. Record indices where they match.

Problem: Sorting each window costs O(k log k). A sliding frequency array updates in O(1) per slide โ€” just add the entering character and remove the leaving one.
Java โ€” Sort Each Window
import java.util.*; class Solution { public List<Integer> findAnagrams(String s, String p) { List<Integer> result = new ArrayList<>(); char[] target = p.toCharArray(); Arrays.sort(target); for (int i = 0; i <= s.length() - p.length(); i++) { char[] window = s.substring(i, i + p.length()).toCharArray(); Arrays.sort(window); if (Arrays.equals(target, window)) result.add(i); } return result; } }
Python โ€” Sort Each Window
class Solution: def findAnagrams(self, s: str, p: str) -> list[int]: target = sorted(p) k = len(p) return [i for i in range(len(s) - k + 1) if sorted(s[i:i+k]) == target]
MetricValue
TimeO(n ยท k log k)
SpaceO(k)
03
Section Three ยท Approach 2

Fixed-Size Sliding Window โ€” O(n)

This problem is nearly identical to LC 567 (Permutation in String) โ€” the difference is we collect all matching indices instead of returning at the first match. Same algorithm: fixed window of size len(p), frequency comparison via a matches counter.

๐Ÿ’ก Mental model: Same cookie-cutter analogy as LC 567. Slide a window of size len(p) across s. At each position, incrementally update the frequency counts. When all 26 character counts match between the window and p, record the starting index.
Algorithm
  • Step 1: Build pCount[26] and initial sCount[26] from first len(p) chars.
  • Step 2: Initialize matches = number of equal slots (0โ€“26).
  • Step 3: If matches == 26, add index 0 to result.
  • Step 4: Slide window: add right char, remove left char, update matches.
  • Step 5: If matches == 26 after a slide, add left + 1 to result.
๐ŸŽฏ LC 438 vs LC 567:
  • These are the same problem โ€” 567 asks "does any anagram exist?" (return bool), 438 asks "where are all anagrams?" (return list of indices).
  • The sliding window logic is identical; only the output collection differs.
04
Section Four ยท Trace

Visual Walkthrough

Trace through s = "cbaebabacd", p = "abc":

Fixed Window of size 3 โ€” slide and check frequency match
p = "abc" โ†’ target: a=1, b=1, c=1 s = c b a e b a b a c d 0 1 2 3 4 5 6 7 8 9 [0,2] "cba" โ†’ {c:1,b:1,a:1} == target โœ“ โ†’ add 0 [1,3] "bae" โ†’ {b:1,a:1,e:1} โ‰  [2,4] "aeb" โ†’ {a:1,e:1,b:1} โ‰  [3,5] "eba" โ†’ {e:1,b:1,a:1} โ‰  [4,6] "bab" โ†’ {b:2,a:1} โ‰  [5,7] "aba" โ†’ {a:2,b:1} โ‰  [6,8] "bac" โ†’ {b:1,a:1,c:1} == target โœ“ โ†’ add 6 [7,9] "acd" โ†’ {a:1,c:1,d:1} โ‰  Answer: [0, 6] โœ“
05
Section Five ยท Implementation

Code โ€” Java & Python

Java โ€” Sliding Window with matches counter
import java.util.*; class Solution { public List<Integer> findAnagrams(String s, String p) { List<Integer> result = new ArrayList<>(); if (p.length() > s.length()) return result; int[] pCount = new int[26], sCount = new int[26]; for (int i = 0; i < p.length(); i++) { pCount[p.charAt(i) - 'a']++; sCount[s.charAt(i) - 'a']++; } int matches = 0; for (int i = 0; i < 26; i++) if (pCount[i] == sCount[i]) matches++; if (matches == 26) result.add(0); for (int r = p.length(); r < s.length(); r++) { int addIdx = s.charAt(r) - 'a'; sCount[addIdx]++; if (sCount[addIdx] == pCount[addIdx]) matches++; else if (sCount[addIdx] == pCount[addIdx] + 1) matches--; int remIdx = s.charAt(r - p.length()) - 'a'; sCount[remIdx]--; if (sCount[remIdx] == pCount[remIdx]) matches++; else if (sCount[remIdx] == pCount[remIdx] - 1) matches--; if (matches == 26) result.add(r - p.length() + 1); } return result; } }
Python โ€” Sliding Window with matches counter
class Solution: def findAnagrams(self, s: str, p: str) -> list[int]: if len(p) > len(s): return [] p_count = [0] * 26 s_count = [0] * 26 for i in range(len(p)): p_count[ord(p[i]) - ord('a')] += 1 s_count[ord(s[i]) - ord('a')] += 1 matches = sum(1 for i in range(26) if p_count[i] == s_count[i]) result = [0] if matches == 26 else [] for r in range(len(p), len(s)): idx = ord(s[r]) - ord('a') s_count[idx] += 1 if s_count[idx] == p_count[idx]: matches += 1 elif s_count[idx] == p_count[idx] + 1: matches -= 1 idx = ord(s[r - len(p)]) - ord('a') s_count[idx] -= 1 if s_count[idx] == p_count[idx]: matches += 1 elif s_count[idx] == p_count[idx] - 1: matches -= 1 if matches == 26: result.append(r - len(p) + 1) return result
06
Section Six ยท Analysis

Complexity Analysis

ApproachTimeSpaceTrade-off
Sort each windowO(n ยท k log k)O(k)Rebuilds sort per slide
Matches counter โ† optimal O(n) O(1) O(1) per slide; 26-slot arrays
07
Section Seven ยท Edge Cases

Edge Cases & Pitfalls

CaseInputWhy It Matters
p longer than ss="ab", p="abc"Empty result โ€” no window possible.
Entire string is anagrams="abc", p="cab"Single result: [0].
Overlapping anagramss="abab", p="ab"[0, 1, 2] โ€” windows overlap. All valid.
All same charss="aaaa", p="aa"[0, 1, 2] โ€” every window of size 2 matches.
No matchs="xyz", p="ab"Empty result.
โš  Common Mistake: Off-by-one in the start index when recording results. After sliding the window right by adding s[r] and removing s[r - len(p)], the new window starts at r - len(p) + 1. Getting this index wrong shifts all results by 1.

โ† Back to Sliding Window problems