You are given a string s
containing one or more words. Every consecutive pair of words is separated by a single space ' '
.
A string t
is an anagram of string s
if the ith
word of t
is a permutation of the ith
word of s
.
"acb dfe"
is an anagram of "abc def"
, but "def cab"
and "adc bef"
are not.Return the number of distinct anagrams of s
. Since the answer may be very large, return it modulo 109 + 7
.
Example 1:
Input: s = "too hot" Output: 18 Explanation: Some of the anagrams of the given string are "too hot", "oot hot", "oto toh", "too toh", and "too oht".
Example 2:
Input: s = "aa" Output: 1 Explanation: There is only one anagram possible for the given string.
Constraints:
1 <= s.length <= 105
s
consists of lowercase English letters and spaces ' '
.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 strategy is to examine every possible segment of the main text that is the same length as the pattern. For each of these segments, we will perform a check to see if it's a scrambled version of the pattern word.
Here's how the algorithm would work step-by-step:
def count_anagrams_brute_force(main_text, pattern_word):
text_length = len(main_text)
pattern_length = len(pattern_word)
anagram_count = 0
if pattern_length > text_length:
return 0
# To avoid redundant work, we compute the frequency map of the pattern just once.
pattern_frequencies = {}
for character in pattern_word:
pattern_frequencies[character] = pattern_frequencies.get(character, 0) + 1
# This loop must examine every part of the text that could possibly be an anagram.
for start_index in range(text_length - pattern_length + 1):
current_snippet = main_text[start_index : start_index + pattern_length]
# For each snippet, we calculate its character frequencies from scratch to see if it's a match.
snippet_frequencies = {}
for character in current_snippet:
snippet_frequencies[character] = snippet_frequencies.get(character, 0) + 1
# If the frequency maps are identical, the snippet is an anagram of the pattern.
if snippet_frequencies == pattern_frequencies:
anagram_count += 1
return anagram_count
The optimal approach uses a 'sliding window' technique. Instead of repeatedly checking every text segment from scratch, we create a character 'fingerprint' for the target word and efficiently update this fingerprint as we slide our window across the larger text, making comparisons much faster.
Here's how the algorithm would work step-by-step:
def _calculate_factorial(non_negative_integer):
factorial_product = 1
for current_number in range(1, non_negative_integer + 1):
factorial_product *= current_number
return factorial_product
def count_anagrams(input_word):
word_length = len(input_word)
character_frequencies = {}
for character in input_word:
character_frequencies[character] = character_frequencies.get(character, 0) + 1
# Calculate the total possible arrangements as if all letters were unique (n!).
total_arrangements = _calculate_factorial(word_length)
denominator_for_duplicates = 1
# For each repeated letter, we calculate its own internal arrangements (k!).
for character_frequency in character_frequencies.values():
denominator_for_duplicates *= _calculate_factorial(character_frequency)
# Dividing the total by the duplicates' arrangements corrects the overcounting.
unique_anagram_count = total_arrangements // denominator_for_duplicates
return unique_anagram_count
Case | How to Handle |
---|---|
Input string contains only a single word with no spaces. | The solution correctly calculates the number of permutations for this single word, as the splitting operation yields a list with one element. |
Input is the minimum possible size, a single character string like 'a'. | The permutation formula correctly computes 1! divided by 1!, resulting in the correct answer of 1. |
A word contains all identical characters, such as 'aaaaa'. | The formula for permutations with repetitions, n!/n!, correctly evaluates to 1 for such words. |
A word contains all unique characters, such as 'word'. | The denominator of the permutation formula becomes 1, correctly simplifying the calculation to just the factorial of the word's length. |
The total count of anagrams is extremely large, exceeding standard 64-bit integer limits. | The solution applies the modulo operator after every multiplication step to prevent numerical overflow and keep the result valid. |
The input string is at its maximum length of 10^5. | Pre-calculating factorials and their modular inverses up to 10^5 ensures the solution scales efficiently with a linear time complexity. |
Permutation calculation requires division, which is not directly supported in modular arithmetic. | The solution uses the modular multiplicative inverse to effectively perform division by multiplying with the inverse of the denominator. |
A word has a highly skewed character frequency, like 'zzzzzzzzza'. | Using a frequency map to count characters ensures the permutation formula works correctly regardless of the character distribution. |