When candidates first encounter this problem, they often recognize that it involves generating combinations but struggle with the mechanics of building strings across multiple groups of characters. The real challenge is understanding how to systematically explore every possible path through a branching decision tree without missing or duplicating any combination. This problem is a textbook exercise in backtracking, which is a core pattern behind subset generation, permutation problems, and constraint satisfaction tasks that appear frequently in interviews.
Problem statement
Given a string containing digits from 2 to 9 inclusive, return all possible letter combinations that the number could represent on a standard telephone keypad. The mapping of digits to letters follows the classic phone layout:
- 2 maps to “abc”
- 3 maps to “def”
- 4 maps to “ghi”
- 5 maps to “jkl”
- 6 maps to “mno”
- 7 maps to “pqrs”
- 8 maps to “tuv”
- 9 maps to “wxyz”
Each digit in the input string contributes one letter to each output combination. The answer can be returned in any order. If the input string is empty, return an empty list.
Note that the digit 1 does not map to any letters on a phone keypad.
Constraints
0 ≤ digits.length ≤ 4digits[i]is a digit in the range['2', '9']
Examples
| Input | Output | Explanation |
|---|---|---|
"23" | ["ad","ae","af","bd","be","bf","cd","ce","cf"] | Digit 2 maps to “abc” and digit 3 maps to “def”. Pairing one letter from each produces 9 combinations. |
"" | [] | An empty input has no digits to map, so the result is an empty list. |
"79" |
| Digit 7 maps to “pqrs” (4 letters) and digit 9 maps to “wxyz” (4 letters), yielding 16 combinations. |
"234" |
| Three digits with 3 letters each produce 27 total combinations. |
"8" | ["t","u","v"] | A single digit simply returns its mapped letters individually. |
Why enumerating all possibilities doesn’t scale
At first glance, you might think about generating every possible string of the correct length and then filtering to keep only valid ones. But the number of strings you would need to check grows dramatically. Each digit contributes 3 or 4 possible letters, so with n digits the total number of valid combinations is up to 4^n. If you tried to enumerate strings over the entire alphabet and then verify each one, you would waste enormous effort on invalid candidates.
Even within the valid combinations, a naive iterative approach that builds partial results using nested loops becomes unwieldy as the number of digits increases. With four digits where each maps to four letters, you would need four nested loops, and the structure would not generalize to arbitrary input lengths. The problem demands a recursive strategy that naturally adapts to any number of digits.
Optimized approach
The ideal strategy is backtracking, which is a controlled form of recursion where you build a candidate solution one decision at a time and undo each decision after fully exploring its consequences. Here, each “decision” is choosing which letter to use for the current digit.
Think of the problem as a tree. The root represents an empty string. At depth 0, you branch into the letters for the first digit. At depth 1, each of those branches splits again into the letters for the second digit, and so on. Every path from root to a leaf at depth n (where n is the number of digits) represents one valid combination. Backtracking walks this entire tree, collecting every leaf.
This pattern fits naturally because:
- Each digit position is an independent choice.
- The choices at one level do not constrain choices at other levels.
- We need every valid combination, not just one.
Why the optimized approach works well
Backtracking visits exactly the combinations that matter. It never generates an invalid partial string because every path through the tree corresponds to a valid selection of one letter per digit. The “undo” step (popping the last letter from the current path) ensures the algorithm reuses a single mutable path structure rather than allocating new strings at every step, keeping memory overhead low.
Because the total output size is O(4^n) and each combination has length n, the algorithm does O(n * 4^n) total work, which matches the size of the output itself. You cannot do better than this since every combination must appear in the result.
Step-by-step algorithm
- If the input string
digitsis empty, return an empty list immediately. - Create a dictionary that maps each digit character (
'2'through'9') to its corresponding list of letters. - Initialize an empty list
combinationsto store the final results. - Define a recursive function
backtrack(index, path)whereindexis the current position indigitsandpathis the list of letters chosen so far. - Inside
backtrack, if the length ofpathequals the length ofdigits, joinpathinto a string, append it tocombinations, and return. - Otherwise, look up the letters corresponding to
digits[index]. - For each letter in that group, append the letter to
path, recursively callbacktrack(index + 1, path), and then remove the last letter frompathto undo the choice. - Start the recursion by calling
backtrack(0, []). - Return
combinations.
Python implementation
Code for Letter Combinations Of A Phone Number
Time complexity
Let n be the length of the input string digits. Each digit maps to either 3 or 4 letters, so in the worst case every digit maps to 4 letters. The total number of combinations is at most 4^n, and each combination is a string of length n. Building and storing each string takes O(n) time. Therefore the overall time complexity is O(n * 4^n). Given the constraint that n ≤ 4, this amounts to at most 4 * 256 = 1024 character operations, which is trivially fast.
Space complexity
The output list stores up to 4^n strings, each of length n, giving O(n * 4^n) space for the results. Beyond the output, the recursion stack goes at most n levels deep, and the mutable path list holds at most n characters. So the auxiliary space (excluding the output) is O(n). With n ≤ 4, the recursion depth is negligible.
Edge cases
- Empty input string: When
digitsis"", there are no digits to process. The function should return an empty list[], not a list containing an empty string. - Single digit: When
digitscontains only one character like"7", the result is simply the individual letters for that digit:["p", "q", "r", "s"]. No cross-product is needed. - All four-letter digits: An input like
"79"(or"7979"at the constraint limit) produces the maximum branching factor at every level. This tests that the solution handles the upper bound of output size correctly.
Common pitfalls
- Returning
[""]instead of[]for empty input: Many candidates forget the empty check and let the recursion produce a single empty string from an empty path, which is incorrect. - Forgetting to pop after the recursive call: Without the undo step, the
pathlist accumulates letters from previous branches, producing garbled results. This is the most common backtracking mistake. - Using string concatenation instead of a list: Building a new string at every recursive call creates unnecessary intermediate objects. Using a mutable list with append and pop is more efficient and idiomatic for backtracking.
- Hardcoding nested loops: Some candidates write nested
forloops to handle two or three digits, which breaks when the input length changes. The recursive approach generalizes to any number of digits.