Letter Combinations of a Phone Number
Table of Contents + β
Think about the old phone keypads. The number 2 had the letters abc printed on it. The number 3 had def. This question takes a string of digits and asks for every word you could type. It is a clean way to practice building all combinations with backtracking. So it shows up in interviews a lot.
π― The Problem
You get a string of digits. Each digit maps to letters, like the old phone keypad. You return every letter string you can build.
The rules:
- The digit
2givesa,b, orc. The digit3givesd,e, orf, and so on. - Pick one letter for each digit, in order.
- Each finished string is one combination. One full pick, one letter per digit.
- An empty input string returns an empty list, not a list with one empty string.
Input: digits = "23"Output: ["ad","ae","af","bd","be","bf","cd","ce","cf"]
Explanation: 2 -> abc, 3 -> defPick one letter from each digit and join them.Here is the digit to letter map shown as a picture. Each digit points to its small set of letters.
π Approach 1: Repeated Cartesian Product (Iterative)
The idea in one line: keep a list of strings so far, and for each new digit grow every string by each of its letters.
How it works:
- Start the list with one empty string.
- Read the next digit. Look up its letters.
- For every string in the list, append every letter to form new strings.
- Replace the list with the new, longer strings.
- Repeat for each digit.
Why it works:
- This is the same set of answers as the recursion, built layer by layer.
- It avoids the call stack.
Why it is heavier:
- It holds all partial strings of the current length in memory at once.
- Backtracking keeps only one path at a time, so it uses less space.
Here is the iterative product code for that idea:
def letter_combinations(digits): if not digits: return []
phone = { "2": "abc", "3": "def", "4": "ghi", "5": "jkl", "6": "mno", "7": "pqrs", "8": "tuv", "9": "wxyz", } result = [""] for digit in digits: result = [prefix + ch for prefix in result for ch in phone[digit]] return resultβ‘ Approach 2: Backtracking (Best)
The idea in one line: build the string one digit at a time, try each letter as a choice, then undo and try the next.
How it works:
- Start with an empty string. Look at the first digit.
- Try each of its letters. Add one, move to the next digit.
- Keep going until one letter sits for every digit. That finished string is one answer.
Why backtracking:
- At each digit there is a small set of letters. That is the choice.
- Picking one letter does not block the others.
- After exploring everything that starts with
a, remove theaand tryb. - Add a letter, recurse, remove the letter. The classic add, recurse, remove pattern.
When a path finishes:
- The position we fill reaches the end of the digit string.
- The built string now has one letter per digit. Save it.
Steps to Solve
- Build a map from each digit to its letters, like 2 to abc and 3 to def.
- Start with an empty string and the first digit position.
- If the position reached the end of the digits, save the built string and stop this path.
- Look up the letters for the current digit.
- For each letter, add it to the built string.
- Recurse to the next digit position.
- After the recursion returns, remove that letter so you can try the next one.
Here is the decision tree for 23. The first level picks a letter for 2. The second level picks a letter for 3.
This Python version maps each digit to its letters in a dictionary and builds the string with backtracking.
def letter_combinations(digits): if not digits: # no digits means no combinations return [] keypad = { "2": "abc", "3": "def", "4": "ghi", "5": "jkl", "6": "mno", "7": "pqrs", "8": "tuv", "9": "wxyz", } result = []
def backtrack(index, current): if index == len(digits): # one letter per digit result.append("".join(current)) return for letter in keypad[digits[index]]: # letters for this digit current.append(letter) # add the letter backtrack(index + 1, current) # move to next digit current.pop() # remove it (backtrack)
backtrack(0, []) return result
print(letter_combinations("23"))The output of the above code will be:
['ad', 'ae', 'af', 'bd', 'be', 'bf', 'cd', 'ce', 'cf']Let us read the Python version line by line. The shape is the classic backtracking template.
if not digits: is a guard. If the input string is empty there is nothing to build. So we return an empty list right away.
keypad = { ... } is our lookup table. Each digit string maps to its letters. This is just the old phone keypad written as a dictionary. We will read from it at each step.
if index == len(digits): is the stop condition. index is the digit position we are filling. When index reaches the length, we have chosen a letter for every digit. So the current list holds one complete combination.
result.append("".join(current)) saves that combination. The join turns the list of letters into one string. We store the string, so it stays safe even when current changes later.
for letter in keypad[digits[index]]: reads the letters for the current digit. digits[index] is the actual digit character. We look it up in the keypad to get its letters. Then we loop over each one.
current.append(letter) makes the choice. backtrack(index + 1, current) moves to the next digit and goes deeper. current.pop() undoes the choice. This pop is the backtrack. It removes the last letter so the loop can try the next one cleanly.
β±οΈ Time and Space Complexity
Each digit has at most four letters, like the 7 and 9 keys. If there are n digits, the number of combinations is up to 4 to the power of n. For each finished combination we also spend O(n) to join the letters into a string. So the time is O(n Γ 4βΏ). The recursion depth and the current string take O(n) space, not counting the output list.
| Approach | Time Complexity | Space Complexity |
|---|---|---|
| Repeated Cartesian product (iterative) | O(n Γ 4βΏ) | O(4βΏ) partial strings |
| Backtracking over digit letters | O(n Γ 4βΏ) | O(n) |
Tip
Do not forget the empty input case. If the digits string is empty, the answer is an empty list, not a list with one empty string. Interviewers love to check this edge.
π§© Key Takeaways
- β Map each digit to its letters first, just like the old phone keypad.
- β Build the string one digit at a time, trying each letter as a choice.
- β When the position reaches the end of the digits, save the built string.
- β Add a letter, recurse to the next digit, then remove the letter to try the next one.
- β Handle the empty input as a special case that returns an empty list.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What does each digit map to in this problem?
Why: Each digit maps to a small group of letters, exactly like the letters printed on an old phone keypad.
- 2
When does one path produce a finished combination?
Why: When the index reaches the length of the digits, one letter has been chosen per digit, so the string is complete.
- 3
What does the pop step do after the recursive call?
Why: Popping the last letter restores the previous state, which is the backtracking step before trying the next letter.
- 4
What should the function return for an empty digits string?
Why: With no digits there is nothing to combine, so the correct answer is an empty list.