Find All Anagrams in a String (opens in a new tab) Medium
Problem
Given a string s
and a non-empty string p
, find all the start indices of p
's anagrams in s
.
Strings consists of lowercase English letters only and the length of both strings s
and p
will not be larger than 20,100.
Example
Input: s = "cbaebabacd", p = "abc"
Output: [0, 6]
Summary
To solve this problem, we can use the sliding window technique along with a character frequency map. We will maintain a window of length p
in the string s
and compare the character frequencies in the current window with the character frequencies of the string p
. If the frequencies match, the starting index of the window is added to the output list.
Solution
In TypeScript
function findAnagrams(s: string, p: string): number[] {
const result: number[] = [];
const pCharCount: Map<string, number> = new Map();
const sCharCount: Map<string, number> = new Map();
for (const char of p) {
pCharCount.set(char, (pCharCount.get(char) || 0) + 1);
}
for (let i = 0; i < s.length; i++) {
const char = s[i];
sCharCount.set(char, (sCharCount.get(char) || 0) + 1);
if (i >= p.length) {
const removeChar = s[i - p.length];
sCharCount.set(removeChar, sCharCount.get(removeChar) - 1);
if (sCharCount.get(removeChar) === 0) {
sCharCount.delete(removeChar);
}
}
if (areMapsEqual(pCharCount, sCharCount)) {
result.push(i - p.length + 1);
}
}
return result;
}
function areMapsEqual(map1: Map<string, number>, map2: Map<string, number>): boolean {
if (map1.size !== map2.size) {
return false;
}
for (const [key, value] of map1) {
if (map2.get(key) !== value) {
return false;
}
}
return true;
}
In Python
def findAnagrams(s: str, p: str) -> List[int]:
result = []
p_char_count = {}
s_char_count = {}
for char in p:
p_char_count[char] = p_char_count.get(char, 0) + 1
for i in range(len(s)):
char = s[i]
s_char_count[char] = s_char_count.get(char, 0) + 1
if i >= len(p):
remove_char = s[i - len(p)]
s_char_count[remove_char] -= 1
if s_char_count[remove_char] == 0:
del s_char_count[remove_char]
if are_maps_equal(p_char_count, s_char_count):
result.append(i - len(p) + 1)
return result
def are_maps_equal(map1, map2):
if len(map1) != len(map2):
return False
for key, value in map1.items():
if map2.get(key) != value:
return False
return True
Step-by-step explanation
- Initialize an empty
result
array to store the starting indices of anagrams found.
const result: number[] = [];
- Initialize two Maps
pCharCount
andsCharCount
to store the character count of patternp
and substrings ofs
.
const pCharCount: Map<string, number> = new Map();
const sCharCount: Map<string, number> = new Map();
- Loop through the characters of
p
, incrementing their count in thepCharCount
map.
for (const char of p) {
pCharCount.set(char, (pCharCount.get(char) || 0) + 1);
}
-
Loop through the characters of
s
with the indexi
.a. Increment the count of the current character
char
in thesCharCount
map.
b. Ifi
is greater than or equal to the length ofp
, remove the characters[i - p.length]
from thesCharCount
map. If the character count becomes zero, delete the entry from the map.
c. Check if thepCharCount
andsCharCount
maps are equal using theareMapsEqual
function. If they are equal, add the starting index of the substring(i - p.length + 1)
to the result array.
for (let i = 0; i < s.length; i++) {
const char = s[i];
sCharCount.set(char, (sCharCount.get(char) || 0) + 1);
if (i >= p.length) {
const removeChar = s[i - p.length];
sCharCount.set(removeChar, sCharCount.get(removeChar) - 1);
if (sCharCount.get(removeChar) === 0) {
sCharCount.delete(removeChar);
}
}
if (areMapsEqual(pCharCount, sCharCount)) {
result.push(i - p.length + 1);
}
}
- Return the result array containing the starting indices of anagrams found.
return result;
The areMapsEqual
function checks if the two given maps have the same key-value pairs:
- Check if the size of the two maps is equal. If not, return
false
.
if (map1.size !== map2.size) {
return false;
}
- Loop through the key-value pairs of
map1
. If the value of the current key inmap2
is not equal to the value inmap1
, returnfalse
.
for (const [key, value] of map1) {
if (map2.get(key) !== value) {
return false;
}
}
- If the function doesn't return false during the loop, return true to indicate that the maps are equal.
return true;
Complexity Analysis
Time Complexity
The time complexity of this solution is O(n), where n is the length of the input string s
. We use a single loop to iterate through the string s
and maintain a window of characters.
Space Complexity
The space complexity of this solution is O(m), where m is the size of the character set. In this case, we use maps to store character frequencies for both p
and the sliding window in s
. The character set is limited to lowercase English letters, resulting in a maximum space complexity of O(26).