Alice is texting Bob using her phone. The mapping of digits to letters is shown in the figure below.
[Image of a phone keypad]
In order to add a letter, Alice has to press the key of the corresponding digit i
times, where i
is the position of the letter in the key.
's'
, Alice has to press '7'
four times. Similarly, to add the letter 'k'
, Alice has to press '5'
twice.'0'
and '1'
do not map to any letters, so Alice does not use them.However, due to an error in transmission, Bob did not receive Alice's text message but received a string of pressed keys instead.
"bob"
, Bob received the string "2266622"
.Given a string pressedKeys
representing the string received by Bob, return the total number of possible text messages Alice could have sent. Since the answer may be very large, return it modulo 10^9 + 7
.
Example 1:
Input: pressedKeys = "22233"
Output: 8
Explanation:
The possible text messages Alice could have sent are:
"aaadd", "abdd", "badd", "cdd", "aaae", "abe", "bae", and "ce".
Since there are 8 possible messages, we return 8.
Example 2:
Input: pressedKeys = "222222222222222222222222222222222222"
Output: 82876089
Explanation:
There are 2082876103 possible text messages Alice could have sent.
Since we need to return the answer modulo 10^9 + 7, we return 2082876103 % (10^9 + 7) = 82876089.
What are the time and space complexities of your solution? Can you think of any edge cases?
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 exhaustively explores all possible combinations of text sequences to find the valid ones. We essentially try every possible sequence, validating each against the problem's constraints. If valid, increment a counter, and continue until all sequences have been evaluated.
Here's how the algorithm would work step-by-step:
def count_number_of_texts_brute_force(pressed_keys: str) -> int:
digit_to_letters = {
'2': ['a', 'b', 'c'],
'3': ['d', 'e', 'f'],
'4': ['g', 'h', 'i'],
'5': ['j', 'k', 'l'],
'6': ['m', 'n', 'o'],
'7': ['p', 'q', 'r', 's'],
'8': ['t', 'u', 'v'],
'9': ['w', 'x', 'y', 'z']
}
number_of_valid_text_sequences = 0
def generate_combinations(index: int, current_text: str):
nonlocal number_of_valid_text_sequences
# Base case: If we've processed all digits
if index == len(pressed_keys):
# Increment count when a combination is complete
number_of_valid_text_sequences += 1
return
digit = pressed_keys[index]
letters = digit_to_letters[digit]
# Recursive step: Explore letter options for the current digit
for letter in letters:
# Explore the next digit
generate_combinations(index + 1, current_text + letter)
# Initiate the recursive process starting with an empty combination
generate_combinations(0, "")
return number_of_valid_text_sequences
The optimal way to count the number of texts is to recognize that each digit can represent a limited number of letters. We can use dynamic programming to build up the solution from smaller subproblems. By reusing previous calculations, we avoid recomputing the same counts multiple times.
Here's how the algorithm would work step-by-step:
def count_number_of_texts(pressed_keys: str) -> int:
modulo = 10**9 + 7
length_of_keys = len(pressed_keys)
dp_table = [0] * (length_of_keys + 1)
dp_table[0] = 1
for i in range(1, length_of_keys + 1):
# Base case: the last key is always one way to form a text
dp_table[i] = dp_table[i - 1]
if i > 1 and pressed_keys[i - 1] == pressed_keys[i - 2]:
dp_table[i] = (dp_table[i] + dp_table[i - 2]) % modulo
# If the same key is pressed thrice, add the ways from i-3
if i > 2 and pressed_keys[i - 1] == pressed_keys[i - 3]:
dp_table[i] = (dp_table[i] + dp_table[i - 3]) % modulo
# Handle keys '7' and '9' which can represent four letters
if (i > 3 and
pressed_keys[i - 1] == pressed_keys[i - 4] and
(pressed_keys[i - 1] == '7' or pressed_keys[i - 1] == '9')):
# Add possibilities for the four letter cases
dp_table[i] = (dp_table[i] + dp_table[i - 4]) % modulo
return dp_table[length_of_keys]
Case | How to Handle |
---|---|
Empty input string | Return 1, as an empty string represents one possible (empty) text message. |
Null input string | Throw an IllegalArgumentException or return 0, depending on the requirements. |
String with a single character | Return the number of letters the digit represents (3 for 2,3,4,5,6,8 and 4 for 7,9). |
String with only '1' or '0' characters | Return 1, as '1' and '0' map to themselves and do not contribute to additional text possibilities. |
Long string with repeating characters (e.g., '2222222222') | Use dynamic programming to avoid exponential time complexity for long repeating sequences, memoizing the counts. |
Input string with mixed valid and invalid characters (e.g., '2a3') | Throw an IllegalArgumentException or filter out invalid characters and process the remaining valid digits only. |
Large input string to test performance (stress test) | Ensure dynamic programming solution is efficient enough to handle inputs with length up to 10^5 within time constraints. |
Integer overflow during calculation of text message counts | Apply modulo operation (10^9 + 7) after each multiplication and addition to prevent integer overflow. |