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.
For example:
Example 1:
Input: s = "cbaebabacd", p = "abc"
Output: [0,6]
Explanation:
The substring with start index = 0 is "cba", which is an anagram of "abc".
The substring with start index = 6 is "bac", which is an anagram of "abc".
Example 2:
Input: s = "abab", p = "ab"
Output: [0,1,2]
Explanation:
The substring with start index = 0 is "ab", which is an anagram of "ab".
The substring with start index = 1 is "ba", which is an anagram of "ab".
The substring with start index = 2 is "ab", which is an anagram of "ab".
Constraints:
1 <= s.length, p.length <= 3 * 10^4
s
and p
consist of lowercase English letters.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force method for finding anagrams is all about checking every single possibility. We slide a window across the main string and, at each position, we painstakingly compare it to the target anagram.
Here's how the algorithm would work step-by-step:
def find_all_anagrams_brute_force(main_string, anagram):
anagram_indices = []
anagram_length = len(anagram)
main_string_length = len(main_string)
for index in range(main_string_length - anagram_length + 1):
substring = main_string[index:index + anagram_length]
# Check if the substring is an anagram
if is_anagram(substring, anagram):
#Record the starting index if anagram
anagram_indices.append(index)
return anagram_indices
def is_anagram(first_string, second_string):
if len(first_string) != len(second_string):
return False
first_string_characters = sorted(first_string)
second_string_characters = sorted(second_string)
#Compare the sorted lists to find anagrams
if first_string_characters == second_string_characters:
return True
return False
The key is to avoid repeatedly checking every possible substring. We only need to track how often each letter appears in the target anagram, and then slide a window across the main string, updating our counts as we go. This allows us to quickly check if an anagram exists at each position.
Here's how the algorithm would work step-by-step:
def find_all_anagrams_in_a_string(main_string, anagram):
anagram_length = len(anagram)
main_string_length = len(main_string)
if anagram_length > main_string_length:
return []
anagram_character_counts = {}
for character in anagram:
anagram_character_counts[character] = anagram_character_counts.get(character, 0) + 1
result_indices = []
window_character_counts = {}
# Initialize the window
for i in range(anagram_length):
window_character_counts[main_string[i]] = window_character_counts.get(main_string[i], 0) + 1
#Check if first window is an anagram
if window_character_counts == anagram_character_counts:
result_indices.append(0)
# Slide the window
for i in range(1, main_string_length - anagram_length + 1):
# Remove the leftmost character from the window
left_character = main_string[i - 1]
window_character_counts[left_character] -= 1
if window_character_counts[left_character] == 0:
del window_character_counts[left_character]
# Add the rightmost character to the window
right_character = main_string[i + anagram_length - 1]
window_character_counts[right_character] = window_character_counts.get(right_character, 0) + 1
# Compare the current window with the anagram
if window_character_counts == anagram_character_counts:
result_indices.append(i)
return result_indices
Case | How to Handle |
---|---|
s or p is null | Throw IllegalArgumentException or return an empty list indicating invalid input. |
s is empty, p is non-empty | Return an empty list since the pattern cannot be found in an empty string. |
p is empty, s is non-empty | Return an empty list since an empty pattern can theoretically be 'found' at every index, but it is not an anagram in the traditional sense. |
length of p is greater than length of s | Return an empty list since an anagram of p cannot exist within s. |
s and p have the same length, but are not anagrams | Return an empty list after confirming the initial window in s is not an anagram of p. |
s and p are very long strings, potentially leading to performance issues | Use a sliding window with a frequency map to achieve O(n) time complexity for efficient processing. |
p contains duplicate characters | The frequency map correctly accounts for duplicate characters within p. |
s contains characters not present in p | The sliding window and comparison of frequency maps will correctly identify when the current window is not an anagram. |