Start learning free

LESSON 359 OF 572 · BACKTRACKING

Letter Combinations of a Phone Number in C#: Backtracking, O(n * 4^n)

Practice O(n · 4n) Read the full explanation

Given a string digits made of the digits 2 to 9, return every letter combination the number could spell on a phone keypad.

Each digit stands for the letters on its key:

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

A combination takes one letter from each digit's key, in the order of the digits. Return the combinations in keypad order: every combination that starts with the first letter of the first digit's key comes before every combination that starts with its second letter, and so on; inside each of those groups, the same rule for the second digit, then the third. (Each key's letters are in alphabetical order, so this is also alphabetical order.)

If digits is empty, there is nothing to spell: return no combinations at all, not one empty string.

 

Example 1:

Input: digits = "23"
Output: ["ad","ae","af","bd","be","bf","cd","ce","cf"]
Explanation: 2 gives a, b or c, and 3 gives d, e or f: 3 × 3 = 9 combinations.
The three that start with a come first, in the order of 3's letters; then the ones that start with b, then c.

Example 2:

Input: digits = ""
Output: []
Explanation: No digits, so there are no combinations.

Example 3:

Input: digits = "7"
Output: ["p","q","r","s"]
Explanation: With one digit, each letter of its key is a combination. The keys 7 and 9 have four letters, the others three.

 

Constraints:

  • 0 <= digits.length <= 4
  • Each character of digits is a digit from '2' to '9'.

Note:

The tutorial uses backtracking. Walk through digits one position at a time, keeping the letters chosen so far in a string current. At position i, try each letter of the key of digits[i], in the key's order: add it to the end of current and go on to position i + 1. When the position reaches the end of digits, every digit has its letter: save current. Trying the letters in the key's order gives exactly the keypad order above. Each call gets its own longer string, so nothing has to be undone when a call returns.

Each key has at most 4 letters, so n digits give at most 4n combinations, and building one takes n steps: the time is O(n · 4n). Besides the answer, the search keeps at most O(n) calls open at a time, one per digit (their strings add up to O(n2) characters at most).

STEP 1 OF 11
    Ctrl + Enter to check

    C# · BACKTRACKING

    Letter Combinations Of A Phone Number in C#, explained

    Problem overview

    The method LetterCombinations receives a string digits made of the characters '2' to '9' and returns a List<string> with every word those keys could spell on a phone keypad. Key 2 carries abc, 3 carries def, and so on up to 9 with wxyz. Keys 7 and 9 have four letters, the others three. A combination takes one letter from each digit's key, in the order of the digits, and the list must be in keypad order: everything that starts with the first letter of the first key comes first, and inside each group the same rule applies to the next digit.

    For "23" the result is ["ad","ae","af","bd","be","bf","cd","ce","cf"]: 3 letters times 3 letters is 9 words. For "7" it is ["p","q","r","s"]. For the empty string "" it is an empty list: nothing to spell means no combinations at all, not one empty word.

    Approach: choose one letter per digit

    Building the words is a decision tree. For the first digit you pick one of its letters. For each pick, you pick one letter for the second digit, and so on. A word is complete when every digit has a letter. Looping over the digits can't express this, because the number of nested loops would change with the length of the input. Recursion handles it: one call settles one digit and calls itself for the next one. This is backtracking.

    A call knows the position i of the digit it must spell and the letters chosen so far, current. If i equals digits.Length, every digit has its letter, so current is a finished word and goes into the result. Otherwise the call tries each letter of the key of digits[i] in the key's order, and for each one calls itself with i + 1 and the letter added. Trying the letters in key order is what produces the keypad order.

    Many backtracking solutions add a choice to a shared structure and remove it after the call. Here nothing needs removing. In C#, current + letter builds a brand-new string, so each recursive call gets its own longer word and current stays unchanged for the next letter. Strings are immutable, so a finished word can go into the result without a copy.

    Step by step

    1. Create result, an empty List<string>.
    2. Return it right away if digits.Length == 0. Without this check the search starts at i == 0 == digits.Length, records the empty string and returns a list with one empty word.
    3. Build the keypad array of ten strings, indexed by digit. Indexes 0 and 1 hold empty strings because those keys carry no letters. They are never used but keep keys 2 to 9 at their own indexes.
    4. Call Backtrack with digits, position 0, the empty string, keypad and result, then return result. Backtrack fills the same list that the caller returns.
    5. Write Backtrack. Its base case checks i == digits.Length, adds current to result and returns.
    6. Look up the letters of the current digit: keypad[digits[i] - '0'].
    7. Loop over those letters with foreach, and for each call Backtrack with i + 1 and current + letter.

    The solution in C#

    class Solution {
        public List<string> LetterCombinations(string digits) {
            List<string> result = new List<string>();
            if (digits.Length == 0) {
                return result;
            }
            string[] keypad = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
            Backtrack(digits, 0, "", keypad, result);
            return result;
        }
    
        public void Backtrack(string digits, int i, string current, string[] keypad, List<string> result) {
            if (i == digits.Length) {
                result.Add(current);
                return;
            }
            string letters = keypad[digits[i] - '0'];
            foreach (char letter in letters) {
                Backtrack(digits, i + 1, current + letter, keypad, result);
            }
        }
    }
    

    The C# details worth knowing:

    • Converting a digit character to a number. digits[i] is a char. The digit characters are consecutive in Unicode, so '7' - '0' is the int 7, while the character '7' on its own has the numeric value 55. Using digits[i] directly as an index would look for position 55 and throw an IndexOutOfRangeException.
    • Adding a char to a string. current + letter with a string on the left is concatenation: "a" + 'd' is "ad". Two char values added together are not concatenated. They become an int: '7' + 1 is 56.
    • foreach over a string gives its characters one at a time, as char.
    • An array initializer. string[] keypad = {"", "", "abc", ...}; declares and fills the array in one statement, and the length (10) is taken from the number of values.
    • The helper changes the caller's list. List<string> is a reference type, so Backtrack and LetterCombinations work on the same list.

    Complexity

    Each key has at most 4 letters, so n digits give at most 4^n words. Each word is built with n concatenations, and each concatenation copies a string of up to n characters.

    Time: O(n * 4^n), counting the work of building the words. With the lesson's limit of 4 digits that is at most 256 words: "7979" gives exactly 256, with "pwpw" first and "szsz" last.

    Space: beyond the answer, O(n): one call per digit on the stack, each holding a string of at most n letters. The result list itself holds up to 4^n words.

    Edge cases and common mistakes

    • Empty input. The result must be empty. Without the length check, "" gives a list holding one empty string.
    • A single digit. "7" gives the four letters of its key, each as its own string.
    • Repeated digits. "22" is valid: both digits use key 2, and the result has nine words.
    • Using the digit character as an index. Forgetting - '0' is the usual error, as above.

    Testing

    • "23" gives the 9 words ["ad","ae","af","bd","be","bf","cd","ce","cf"].
    • "" gives [].
    • "7" gives ["p","q","r","s"].
    • "79" gives 16 words, from "pw" to "sz".
    • "234" gives 27 words, from "adg" to "cfi".
    • "7979" gives 256 words.
    Keep going with C#500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free