You are given a string containing digits from 2 to 9 inclusive. Each digit maps to a set of letters as on a traditional phone keypad:

1

2 abc

3 def

4 ghi

5 jkl

6 mno

7 pqrs

8 tuv

9 wxyz

Return all possible letter combinations the input digits could represent.

Example:

Input: digits = '69'
Output: ['mw', 'mx', 'my', 'mz', 'nw', 'nx', 'ny', 'nz', 'ow', 'ox', 'oy', 'oz']

In Plain English

New to this one? Start here. Below is the core idea in everyday language, plus a real-world analogy—then the sections that follow build on it with the formal reasoning, code, and step-by-step walkthroughs.

📱 Real-world analogy: an old-school text message

On a numeric keypad, one digit can mean several letters — pressing 6 could be m, n, or o. If someone hands you a number and asks “what words could this spell?”, you must try every letter of the first digit paired with every letter of the second, and so on. That “every-with-every” is a cartesian product, and backtracking walks it neatly.

One Big Idea

A combination is one choice per digit. Map each digit to its letters, then build the answer by choosing a letter for digit 0, a letter for digit 1, and so on. When you have chosen a letter for every digit, you have one complete word to record.

Intuition

At each digit in the string, we have a decision to make: which letter will this digit represent? Based on this decision, let's illustrate the state space tree that represents the choices at each digit of the input string.

State space tree
Consider the input string "69". Let's start with the root node of the tree, which is an empty string:

Image represents a simplified diagram showing four vertical, rectangular blocks arranged horizontally with small gaps between them.  There are no labels, URLs, parameters, or other textual information present within or around the blocks.  The blocks are all identical in appearance, being solid black rectangles of seemingly equal size and shape.  The arrangement suggests a linear or sequential process, with information potentially flowing from left to right, although no arrows or other indicators of direction or data flow are visible. The image lacks any further detail or context to specify the nature of the blocks or their interaction.

At the first digit, 6, we have the choice of starting our combination with 'm', 'n', or 'o':

Image represents a decision tree illustrating a choice process.  A central node labeled '''' (empty string) branches into three directions.  The top-most branch, labeled 'choose m' in orange text, points left to a terminal node labeled `'m'` (representing the character 'm'). The bottom branch, labeled 'choose n' in orange text, points downwards to a terminal node labeled `'n'` (representing the character 'n'). The right-most branch, labeled 'choose o' in orange text, points right to a terminal node labeled `'o'` (representing the character 'o').  The leftmost node is further labeled 'digit 6: 'm'' indicating that this diagram might be part of a larger process related to digit 6, where 'm' is a possible outcome.  The arrows indicate the flow of information or the decision path taken, with each branch representing a choice between different characters ('m', 'n', or 'o').

For each of these combinations we've created, we now have a new decision to make: which letter of digit 9 ('w', 'x', 'y', 'z') should we choose? These choices are illustrated below:

Image represents a tree-like structure illustrating a coding pattern.  The root node is labeled with double quotes (' '), from which three branches extend, each labeled with a single character: 'm' (in orange), 'n' (in orange), and 'o' (in orange). Each of these nodes then branches further into four child nodes, each labeled with a two-character string.  The first character in each child node's label is inherited from its parent node ('m', 'n', or 'o'), while the second character is one of 'w', 'x', 'y', or 'z' (all in orange), resulting in combinations like 'mw', 'mx', 'my', 'mz', 'nw', 'nx', 'ny', 'nz', 'ow', 'ox', 'oy', and 'oz'.  The left side of the image is labeled 'digit 6:' and the bottom left 'digit 9:', suggesting a relationship between the tree structure and these digits, possibly representing a coding scheme where digit 6 maps to the three parent nodes ('m', 'n', 'o') and digit 9 maps to the twelve leaf nodes (the two-character strings).  The arrows indicate the flow of information from parent nodes to their respective children.

One important thing missing from this state space tree is information on which digit we're making a decision on at each node. We can use an index i to determine which digit we're considering at each node:

Image represents a tree-like structure illustrating a coding pattern, possibly related to string manipulation or code generation.  The diagram's top shows a root node labeled  “'' representing an initial state. From this root, three branches descend, each labeled with a single character: 'm', 'n', and 'o'. Each of these nodes further branches into four child nodes, each representing a two-character string.  For example, the 'm' node branches into 'mw', 'mx', 'my', and 'mz'. Similarly, 'n' branches into 'nw', 'nx', 'ny', and 'nz', and 'o' branches into 'ow', 'ox', 'oy', and 'oz'.  To the left of the main tree, three separate boxes are shown, each labeled 'i = 0', 'i = 1', and 'i = 2' respectively, with a downward arrow pointing to 'digit = '69'' in each case. This suggests an iterative process where the value of 'i' increments, and the 'digit' remains constant at '69' throughout the iterations. The overall structure suggests a recursive or combinatorial generation of strings based on an initial input ('69') and an iterative counter ('i').

The final level of this decision tree (i.e., when i == n, where n denotes the length of the input string) represents all possible combinations that can be created from the provided string. Similar to our approach in Find All Subsets, let's use backtracking to obtain these keypad combinations.

Mapping digits to letters
The final thing we need to figure out is a way to determine which letters correspond to which digits. A hash map is great for this purpose. In the hash map, digits are the keys, and the associated sets of letters are their values. This allows us to access the letters in constant time:

Image represents a table titled 'keypad_map' that maps digits to corresponding letters on a telephone keypad.  The table is divided into two columns. The left column, labeled 'digit' at the bottom, lists the digits '2' through '9', each enclosed in single quotes. The right column, labeled 'letters' at the bottom, shows the corresponding letters associated with each digit on a standard telephone keypad, also enclosed in double quotes.  For example, the digit '2' maps to the letters 'abc', '3' maps to 'def', and so on, up to '9' mapping to 'wxyz'.  There is no explicit information flow depicted; the table simply presents a static mapping between digits and letter combinations.

Implementation

def phone_keypad_combinations(digits: str) -> List[str]:
    keypad_map = {
        '2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl',
        '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'
    }
    res = []
    backtrack(0, [], digits, keypad_map, res)
    return res

def backtrack(i: int, curr_combination: List[str], digits: str,
             keypad_map: Dict[str, str], res: List[str]) -> None:
    # Termination condition: if all digits have been considered, add the
    # current combination to the output list.
    if len(curr_combination) == len(digits):
        res.append("".join(curr_combination))
        return
    for letter in keypad_map[digits[i]]:
       # Add the current letter.
        curr_combination.append(letter)
        # Recursively explore all paths that branch from this combination.
        backtrack(i + 1, curr_combination, digits, keypad_map, res)
        # Backtrack by removing the letter we just added.
        curr_combination.pop()
#include <vector>
#include <string>
#include <unordered_map>
using namespace std;

void backtrack(int i, string& current, string& digits,
               unordered_map<char, string>& keypad, vector<string>& res) {
    if ((int)current.size() == (int)digits.size()) {   // chose a letter per digit
        res.push_back(current);                        // record a COPY
        return;
    }
    string letters = keypad[digits[i]];
    for (int c = 0; c < (int)letters.size(); c++) {
        current.push_back(letters[c]);                 // choose
        backtrack(i + 1, current, digits, keypad, res);
        current.pop_back();                            // undo
    }
}

vector<string> phoneKeypadCombinations(string digits) {
    if (digits.empty()) {
        return {};
    }
    unordered_map<char, string> keypad = {
        {'2', "abc"}, {'3', "def"}, {'4', "ghi"}, {'5', "jkl"},
        {'6', "mno"}, {'7', "pqrs"}, {'8', "tuv"}, {'9', "wxyz"}
    };
    vector<string> res;
    string current;
    backtrack(0, current, digits, keypad, res);
    return res;
}
// These methods live inside a class, e.g. class Solution { ... }
List<String> phoneKeypadCombinations(String digits) {
    if (digits.isEmpty()) {
        return new ArrayList<>();
    }
    Map<Character, String> keypad = new HashMap<>();
    keypad.put('2', "abc"); keypad.put('3', "def"); keypad.put('4', "ghi");
    keypad.put('5', "jkl"); keypad.put('6', "mno"); keypad.put('7', "pqrs");
    keypad.put('8', "tuv"); keypad.put('9', "wxyz");
    List<String> res = new ArrayList<>();
    backtrack(0, new StringBuilder(), digits, keypad, res);
    return res;
}

void backtrack(int i, StringBuilder current, String digits,
               Map<Character, String> keypad, List<String> res) {
    if (current.length() == digits.length()) {   // chose a letter per digit
        res.add(current.toString());             // record a COPY
        return;
    }
    String letters = keypad.get(digits.charAt(i));
    for (int c = 0; c < letters.length(); c++) {
        current.append(letters.charAt(c));       // choose
        backtrack(i + 1, current, digits, keypad, res);
        current.deleteCharAt(current.length() - 1);   // undo
    }
}

Complexity Analysis

Time complexity: The time complexity of phone_keypad_combinations is O(n4n)O(n\cdot 4^n). This is because the state space tree will branch down until a decision is made for all nn elements. This results in a tree of height nn with a branching factor of 4 since there are up to 4 decisions we can make at each digit. For each of the 4n4^n combinations created, we convert it into a string and add it to the output list, which takes O(n)O(n) time per combination. This results in a total time complexity of O(n4n)O(n\cdot 4^n).

Space complexity: The space complexity is O(n)O(n) due to the recursive call stack, which can grow up to a maximum depth of nn. The keypad_map only takes constant space since there are only 8 key-value pairs.

Dry Runs

Two step-by-step walkthroughs of the algorithm below: a valid case (the expected happy path) and an invalid / edge case that exercises the tricky parts. Each step shows the program state as the code runs—the same steps apply to Python, C++ and Java.

✅ Dry run 1 (valid) — digits = "69".

The recursion drives the last digit fastest: it fixes m for digit 6, then cycles w, x, y, z for digit 9, then moves to n, and so on.

Step 1. Choose m for digit 6, then w for digit 9 → length 2 → record "mw".
Step 2. Undo w, choose x → record "mx"; then y, z"my", "mz".
Step 3. Undo back to digit 6, choose n, repeat the four letters, then o → 12 words total.

The same run as a call table

The first digit's letter stays fixed while the second digit cycles through all of its letters.
digit 6digit 9word recorded
mw,x,y,zmw, mx, my, mz
nw,x,y,znw, nx, ny, nz
ow,x,y,zow, ox, oy, oz

3 letters × 4 letters = 12 combinations. ✓

❌ Dry run 2 (edge) — digits = "", nothing to spell.

With no digits there are no choices to make, so there is no word to build. The guard returns an empty list right away.

Empty input is a special case: return [], not [""].
digitsactionresult
""guard: no digits[]
"2"one digit → 3 letters[a, b, c]

Returning [""] for empty input is the classic bug; the expected answer is the empty list []. ✗

The same steps in each language

The keypad is a dict. Build the word as a list of chars and "".join(current) to record. append / pop are choose / undo.

unordered_map<char,string> maps digit to letters. current is a string; push_back / pop_back choose and undo.

Map<Character,String> for the keypad. Use a StringBuilder as the working word; append / deleteCharAt choose and undo.

Interview Tip

Tip: Check if you can skip trivial implementations.

During an interview, it's crucial to manage your time effectively. If you encounter a trivial and time-consuming task, such as creating the keypad_map in this problem, it's possible the interviewer may allow you to skip it or implement it later if there's time left in the interview. Ensure you at least briefly mention how the implementation you're skipping would work before requesting to move on to the core logic of the problem.