Bird
Raised Fist0
Interview PrepbacktrackingmediumAmazonFacebookGoogleBloomberg

Letter Combinations of Phone Number

Choose your preparation mode4 modes available
🎯
Letter Combinations of Phone Number
mediumBACKTRACKINGAmazonFacebookGoogle

Imagine you have an old phone keypad and want to find all possible letter combinations that a given digit string could represent, like texting on a classic phone.

💡 This problem is a classic example of backtracking where you build all possible combinations by exploring choices at each step. Beginners often struggle because they try to solve it iteratively or get confused about how to explore all branches systematically.
📋
Problem Statement

Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent. Return the answer in any order. The mapping of digits to letters is the same as on the telephone buttons (2 maps to 'abc', 3 to 'def', etc.). Note that digit '1' does not map to any letters.

1 ≤ digits.length ≤ 4digits[i] is a digit in the range ['2', '9']
💡
Example
Input"23"
Outputadaeafbdbebfcdcecf

Digit '2' maps to 'abc', digit '3' maps to 'def'. All combinations of letters from these two sets are returned.

  • Empty input string → returns empty list
  • Single digit input like '2' → returns letters mapped to that digit
  • Input with repeated digits like '22' → returns combinations with repeated letters
  • Maximum length input '2345' → returns all combinations from four digits
⚠️
Common Mistakes
Not handling empty input string

Code may crash or return incorrect results

✅ Add early return for empty input

Using string concatenation in recursion without optimization

Excessive memory usage and slower performance

✅ Use mutable string builder or list to build strings

Not backtracking properly (forgetting to remove last character)

Incorrect combinations or duplicates

✅ Remove last character after recursive call to backtrack

Assuming digit '1' or '0' maps to letters

Key errors or invalid output

✅ Validate input digits or skip digits without mappings

Not testing with repeated digits

Missed bugs in combination generation

✅ Test inputs like '22' to ensure repeated digits handled correctly

🧠
Brute Force (Pure Recursion)
💡 Starting with brute force helps understand the problem deeply by exploring all possible letter combinations without optimization. It builds intuition for backtracking.

Intuition

At each digit, try every possible letter it maps to, then recursively do the same for the next digit until all digits are processed.

Algorithm

  1. Create a mapping from digits to letters.
  2. Define a recursive function that takes the current index and the current combination string.
  3. If the current index equals the length of digits, add the combination to results.
  4. Otherwise, for each letter mapped from the current digit, recurse with the next index and updated combination.
💡 The recursion builds combinations step-by-step, and the base case collects complete combinations.
</>
Code
def letterCombinations(digits):
    if not digits:
        return []
    digit_map = {'2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl',
                 '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'}
    result = []
    def backtrack(index, path):
        if index == len(digits):
            result.append(path)
            return
        for char in digit_map[digits[index]]:
            backtrack(index + 1, path + char)
    backtrack(0, '')
    return result

# Example usage:
if __name__ == '__main__':
    print(letterCombinations('23'))
Line Notes
if not digits:Handle empty input early to avoid unnecessary processing and return empty list
digit_map = {...}Map digits to their corresponding letters as per phone keypad to know possible letters
result = []Initialize list to store all possible letter combinations
def backtrack(index, path):Define recursive helper to build combinations by exploring letters at each digit
if index == len(digits):Base case: all digits processed, add current combination to results
for char in digit_map[digits[index]]:Try each letter mapped from current digit to explore all branches
backtrack(index + 1, path + char)Recurse to next digit with updated combination string
import java.util.*;
public class Solution {
    public List<String> letterCombinations(String digits) {
        if (digits == null || digits.length() == 0) return new ArrayList<>();
        String[] digitMap = new String[]{"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        List<String> result = new ArrayList<>();
        backtrack(digits, 0, new StringBuilder(), digitMap, result);
        return result;
    }
    private void backtrack(String digits, int index, StringBuilder path, String[] digitMap, List<String> result) {
        if (index == digits.length()) {
            result.add(path.toString());
            return;
        }
        String letters = digitMap[digits.charAt(index) - '0'];
        for (char c : letters.toCharArray()) {
            path.append(c);
            backtrack(digits, index + 1, path, digitMap, result);
            path.deleteCharAt(path.length() - 1);
        }
    }
    public static void main(String[] args) {
        Solution sol = new Solution();
        System.out.println(sol.letterCombinations("23"));
    }
}
Line Notes
if (digits == null || digits.length() == 0)Check for empty input to return early and avoid unnecessary processing
String[] digitMap = ...Array maps digit characters to their letters for quick lookup
List<String> result = new ArrayList<>();Initialize list to store all letter combinations
backtrack(digits, 0, new StringBuilder(), digitMap, result);Start recursion from first digit with empty path builder
if (index == digits.length())Base case: all digits processed, add current combination to results
String letters = digitMap[digits.charAt(index) - '0'];Get letters mapped from current digit
for (char c : letters.toCharArray())Iterate over letters mapped from current digit to explore all options
path.append(c);Add current letter to path before recursion
backtrack(digits, index + 1, path, digitMap, result);Recurse to next digit with updated path
path.deleteCharAt(path.length() - 1);Backtrack by removing last letter after recursion to explore other branches
#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Solution {
public:
    vector<string> letterCombinations(string digits) {
        if (digits.empty()) return {};
        vector<string> digitMap = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        vector<string> result;
        string path;
        backtrack(digits, 0, path, digitMap, result);
        return result;
    }
private:
    void backtrack(const string& digits, int index, string& path, const vector<string>& digitMap, vector<string>& result) {
        if (index == digits.size()) {
            result.push_back(path);
            return;
        }
        string letters = digitMap[digits[index] - '0'];
        for (char c : letters) {
            path.push_back(c);
            backtrack(digits, index + 1, path, digitMap, result);
            path.pop_back();
        }
    }
};

int main() {
    Solution sol;
    vector<string> res = sol.letterCombinations("23");
    for (auto& s : res) cout << s << " ";
    cout << endl;
    return 0;
}
Line Notes
if (digits.empty()) return {};Return empty vector immediately if input is empty to avoid processing
vector<string> digitMap = {...};Map digits to corresponding letters for quick lookup
vector<string> result;Initialize vector to store all letter combinations
string path;Mutable string to build current combination efficiently
void backtrack(...)Recursive helper function to build combinations by exploring letters at each digit
if (index == digits.size())Base case: all digits processed, add current path to results
string letters = digitMap[digits[index] - '0'];Get letters mapped from current digit
for (char c : letters)Try each letter mapped from current digit to explore all branches
path.push_back(c);Add letter to path before recursion
backtrack(digits, index + 1, path, digitMap, result);Recurse to next digit with updated path
path.pop_back();Backtrack by removing last letter after recursion to explore other branches
var letterCombinations = function(digits) {
    if (!digits.length) return [];
    const digitMap = {
        '2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl',
        '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'
    };
    const result = [];
    function backtrack(index, path) {
        if (index === digits.length) {
            result.push(path);
            return;
        }
        for (const char of digitMap[digits[index]]) {
            backtrack(index + 1, path + char);
        }
    }
    backtrack(0, '');
    return result;
};

// Example usage:
console.log(letterCombinations('23'));
Line Notes
if (!digits.length) return [];Handle empty input by returning empty array early
const digitMap = {...};Object maps digits to their letters for quick lookup
const result = [];Initialize array to store all letter combinations
function backtrack(index, path)Recursive function to build combinations by exploring letters at each digit
if (index === digits.length)Base case: all digits processed, add current combination to results
for (const char of digitMap[digits[index]])Iterate over letters mapped from current digit to explore all options
backtrack(index + 1, path + char);Recurse to next digit with updated combination string
⏱
Complexity
TimeO(4^n * n)
SpaceO(n)

Each digit can map up to 4 letters (e.g., '7' or '9'), so total combinations are up to 4^n. Each combination is length n, so appending or creating strings takes O(n) time per combination. The recursion stack and path use O(n) space.

💡 For n=3 digits, this means up to 64 combinations, each length 3, so roughly 192 operations to build all strings. The recursion depth is n, so space is proportional to input length.
Interview Verdict: Accepted

Though brute force explores all combinations, it is efficient enough for typical input sizes and is a solid baseline solution.

🧠
Backtracking with StringBuilder / Mutable Path
💡 Using a mutable string builder or similar structure avoids creating new strings at each recursion, improving performance and reducing memory overhead.

Intuition

Instead of concatenating strings (which creates new strings), append and remove characters in place to build combinations.

Algorithm

  1. Create digit to letters mapping.
  2. Use a mutable string builder to build combinations.
  3. At each recursion, append a letter, recurse, then remove the letter to backtrack.
  4. Add complete combinations to results when all digits are processed.
💡 This approach is similar to brute force but uses in-place modification to save memory and time.
</>
Code
def letterCombinations(digits):
    if not digits:
        return []
    digit_map = {'2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl',
                 '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'}
    result = []
    path = []
    def backtrack(index):
        if index == len(digits):
            result.append(''.join(path))
            return
        for char in digit_map[digits[index]]:
            path.append(char)
            backtrack(index + 1)
            path.pop()
    backtrack(0)
    return result

# Example usage:
if __name__ == '__main__':
    print(letterCombinations('23'))
Line Notes
if not digits:Return empty list early if input is empty to avoid unnecessary processing
digit_map = {...}Map digits to their corresponding letters for quick lookup
result = []Initialize list to store all letter combinations
path = []Use list to build string efficiently by appending/removing characters in place
def backtrack(index):Recursive function now uses mutable path without passing it explicitly
if index == len(digits):Base case: join path list into string and add to results
for char in digit_map[digits[index]]:Try each letter mapped from current digit to explore all options
path.append(char)Add letter to path before recursion to build combination
backtrack(index + 1)Recurse to next digit with updated path
path.pop()Remove last letter to backtrack after recursion and explore other branches
import java.util.*;
public class Solution {
    public List<String> letterCombinations(String digits) {
        if (digits == null || digits.length() == 0) return new ArrayList<>();
        String[] digitMap = new String[]{"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        List<String> result = new ArrayList<>();
        StringBuilder path = new StringBuilder();
        backtrack(digits, 0, path, digitMap, result);
        return result;
    }
    private void backtrack(String digits, int index, StringBuilder path, String[] digitMap, List<String> result) {
        if (index == digits.length()) {
            result.add(path.toString());
            return;
        }
        String letters = digitMap[digits.charAt(index) - '0'];
        for (char c : letters.toCharArray()) {
            path.append(c);
            backtrack(digits, index + 1, path, digitMap, result);
            path.deleteCharAt(path.length() - 1);
        }
    }
    public static void main(String[] args) {
        Solution sol = new Solution();
        System.out.println(sol.letterCombinations("23"));
    }
}
Line Notes
if (digits == null || digits.length() == 0)Check for empty input to return early and avoid unnecessary processing
String[] digitMap = ...Array maps digit characters to their letters for quick lookup
List<String> result = new ArrayList<>();Initialize list to store all letter combinations
StringBuilder path = new StringBuilder();Use mutable string builder to avoid string concatenation overhead
backtrack(digits, 0, path, digitMap, result);Pass mutable path to recursive function to build combinations in place
if (index == digits.length())Base case: all digits processed, add current combination to results
String letters = digitMap[digits.charAt(index) - '0'];Get letters mapped from current digit
for (char c : letters.toCharArray())Iterate over letters mapped from current digit to explore all options
path.append(c);Append letter before recursion to build combination
backtrack(digits, index + 1, path, digitMap, result);Recurse to next digit with updated path
path.deleteCharAt(path.length() - 1);Remove last letter to backtrack after recursion and explore other branches
#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Solution {
public:
    vector<string> letterCombinations(string digits) {
        if (digits.empty()) return {};
        vector<string> digitMap = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        vector<string> result;
        string path;
        backtrack(digits, 0, path, digitMap, result);
        return result;
    }
private:
    void backtrack(const string& digits, int index, string& path, const vector<string>& digitMap, vector<string>& result) {
        if (index == digits.size()) {
            result.push_back(path);
            return;
        }
        string letters = digitMap[digits[index] - '0'];
        for (char c : letters) {
            path.push_back(c);
            backtrack(digits, index + 1, path, digitMap, result);
            path.pop_back();
        }
    }
};

int main() {
    Solution sol;
    vector<string> res = sol.letterCombinations("23");
    for (auto& s : res) cout << s << " ";
    cout << endl;
    return 0;
}
Line Notes
if (digits.empty()) return {};Return empty vector immediately if input is empty to avoid processing
vector<string> digitMap = {...};Map digits to corresponding letters for quick lookup
vector<string> result;Initialize vector to store all letter combinations
string path;Mutable string to build current combination efficiently
void backtrack(...)Recursive helper uses mutable path to build combinations in place
if (index == digits.size())Base case: all digits processed, add current path to results
string letters = digitMap[digits[index] - '0'];Get letters mapped from current digit
for (char c : letters)Try each letter mapped from current digit to explore all branches
path.push_back(c);Add letter to path before recursion
backtrack(digits, index + 1, path, digitMap, result);Recurse to next digit with updated path
path.pop_back();Remove letter after recursion to backtrack and explore other branches
var letterCombinations = function(digits) {
    if (!digits.length) return [];
    const digitMap = {
        '2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl',
        '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'
    };
    const result = [];
    const path = [];
    function backtrack(index) {
        if (index === digits.length) {
            result.push(path.join(''));
            return;
        }
        for (const char of digitMap[digits[index]]) {
            path.push(char);
            backtrack(index + 1);
            path.pop();
        }
    }
    backtrack(0);
    return result;
};

console.log(letterCombinations('23'));
Line Notes
if (!digits.length) return [];Return empty array early if input is empty to avoid unnecessary processing
const digitMap = {...};Object maps digits to their letters for quick lookup
const result = [];Initialize array to store all letter combinations
const path = [];Use array as mutable path to build strings efficiently by appending/removing characters
function backtrack(index)Recursive function uses mutable path without passing it explicitly
if (index === digits.length)Base case: join path array into string and add to results
for (const char of digitMap[digits[index]])Iterate over letters mapped from current digit to explore all options
path.push(char);Add letter before recursion to build combination
backtrack(index + 1);Recurse to next digit with updated path
path.pop();Remove letter after recursion to backtrack and explore other branches
⏱
Complexity
TimeO(4^n * n)
SpaceO(n)

Same as brute force but reduces overhead of string concatenation by using mutable path. Each digit can map up to 4 letters, so total combinations are up to 4^n. Each combination is length n, so building strings takes O(n) time per combination. The recursion stack and path use O(n) space.

💡 For n=3, this approach is faster and uses less memory than naive string concatenation because it modifies the path in place instead of creating new strings.
Interview Verdict: Accepted

This is a practical optimization that interviewers appreciate, showing attention to detail and efficiency.

🧠
Iterative Approach Using Queue
💡 An iterative approach uses a queue to build combinations level by level, which can be easier to understand for those uncomfortable with recursion.

Intuition

Start with an empty string in a queue, then for each digit, append all possible letters to each string in the queue, updating the queue iteratively.

Algorithm

  1. Initialize a queue with an empty string.
  2. For each digit, dequeue all current strings and enqueue new strings formed by appending each letter mapped from the digit.
  3. After processing all digits, the queue contains all possible combinations.
  4. Return the queue as a list.
💡 This approach simulates the recursive tree using a queue and builds combinations level by level.
</>
Code
from collections import deque

def letterCombinations(digits):
    if not digits:
        return []
    digit_map = {'2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl',
                 '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'}
    queue = deque([''])
    for digit in digits:
        letters = digit_map[digit]
        for _ in range(len(queue)):
            prev = queue.popleft()
            for char in letters:
                queue.append(prev + char)
    return list(queue)

# Example usage:
if __name__ == '__main__':
    print(letterCombinations('23'))
Line Notes
from collections import dequeUse deque for efficient queue operations (O(1) pops from front)
if not digits:Return empty list early if input is empty to avoid unnecessary processing
digit_map = {...}Map digits to their corresponding letters for quick lookup
queue = deque([''])Initialize queue with empty string as starting point for building combinations
for digit in digits:Process each digit one by one to build combinations level by level
for _ in range(len(queue))Process all current combinations before adding new letters to avoid infinite loop
prev = queue.popleft()Remove front combination to extend with letters from current digit
queue.append(prev + char)Add new combination with appended letter to queue
import java.util.*;
public class Solution {
    public List<String> letterCombinations(String digits) {
        if (digits == null || digits.length() == 0) return new ArrayList<>();
        String[] digitMap = new String[]{"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        LinkedList<String> queue = new LinkedList<>();
        queue.add("");
        for (int i = 0; i < digits.length(); i++) {
            String letters = digitMap[digits.charAt(i) - '0'];
            int size = queue.size();
            for (int j = 0; j < size; j++) {
                String prev = queue.poll();
                for (char c : letters.toCharArray()) {
                    queue.add(prev + c);
                }
            }
        }
        return queue;
    }
    public static void main(String[] args) {
        Solution sol = new Solution();
        System.out.println(sol.letterCombinations("23"));
    }
}
Line Notes
if (digits == null || digits.length() == 0)Check for empty input to return early and avoid unnecessary processing
String[] digitMap = ...Array maps digit characters to their letters for quick lookup
LinkedList<String> queue = new LinkedList<>();Use linked list as queue for BFS to efficiently add/remove elements
queue.add("");Start with empty string in queue as initial combination
for (int i = 0; i < digits.length(); i++)Iterate over each digit to build combinations level by level
int size = queue.size();Store current queue size to process only existing combinations this round
String prev = queue.poll();Remove front string to extend with letters from current digit
queue.add(prev + c);Add new string with appended letter to queue
#include <iostream>
#include <vector>
#include <string>
#include <queue>
using namespace std;

class Solution {
public:
    vector<string> letterCombinations(string digits) {
        if (digits.empty()) return {};
        vector<string> digitMap = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        queue<string> q;
        q.push("");
        for (char d : digits) {
            string letters = digitMap[d - '0'];
            int size = q.size();
            for (int i = 0; i < size; i++) {
                string prev = q.front(); q.pop();
                for (char c : letters) {
                    q.push(prev + c);
                }
            }
        }
        vector<string> result;
        while (!q.empty()) {
            result.push_back(q.front());
            q.pop();
        }
        return result;
    }
};

int main() {
    Solution sol;
    vector<string> res = sol.letterCombinations("23");
    for (auto& s : res) cout << s << " ";
    cout << endl;
    return 0;
}
Line Notes
if (digits.empty()) return {};Return empty vector immediately if input is empty to avoid processing
vector<string> digitMap = {...};Map digits to corresponding letters for quick lookup
queue<string> q;Use queue to hold partial combinations for BFS
q.push("");Initialize queue with empty string as starting point
for (char d : digits)Process each digit to build combinations level by level
int size = q.size();Store current queue size to process only existing combinations this round
string prev = q.front(); q.pop();Remove front string to extend with letters from current digit
q.push(prev + c);Add new string with appended letter to queue
var letterCombinations = function(digits) {
    if (!digits.length) return [];
    const digitMap = {
        '2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl',
        '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'
    };
    let queue = [''];
    for (const digit of digits) {
        const letters = digitMap[digit];
        const newQueue = [];
        for (const prev of queue) {
            for (const char of letters) {
                newQueue.push(prev + char);
            }
        }
        queue = newQueue;
    }
    return queue;
};

console.log(letterCombinations('23'));
Line Notes
if (!digits.length) return [];Return empty array early if input is empty to avoid unnecessary processing
const digitMap = {...};Object maps digits to their letters for quick lookup
let queue = [''];Initialize queue with empty string as starting point
for (const digit of digits)Process each digit iteratively to build combinations level by level
const newQueue = [];Temporary array to hold new combinations for current digit
for (const prev of queue)Iterate over existing partial combinations
newQueue.push(prev + char);Append each letter to existing combinations to form new ones
queue = newQueue;Update queue with new combinations for next digit
⏱
Complexity
TimeO(4^n * n)
SpaceO(4^n * n)

Each digit can map up to 4 letters, so total combinations are up to 4^n. Each string is length n, so total space and time scale accordingly. The queue holds all partial combinations, which can be up to 4^n in number, each of length up to n.

💡 For n=3, this means up to 64 strings of length 3, so about 192 characters processed. This approach avoids recursion stack overhead by building combinations iteratively.
Interview Verdict: Accepted

This approach is iterative and avoids recursion stack overhead, useful in some interview contexts.

📊
All Approaches - One-Glance Tradeoffs
💡 Backtracking with mutable path is the best balance of clarity and efficiency and is recommended for interviews.
ApproachTimeSpaceStack RiskReconstructUse In Interview
1. Brute Force (Pure Recursion)O(4^n * n)O(n)Yes (deep recursion for large n)YesMention only - good for explaining basics
2. Backtracking with Mutable PathO(4^n * n)O(n)YesYesCode this approach in 95% of interviews
3. Iterative Using QueueO(4^n * n)O(4^n * n)NoYesMention as alternative, rarely code
💼
Interview Strategy
💡 Use this guide to understand the problem deeply, practice coding all approaches, and prepare to explain tradeoffs clearly in interviews.

How to Present

Clarify input constraints and ask about empty input.State the brute force recursive approach to generate all combinations.Explain how to optimize with mutable path to reduce string creation.Mention iterative approach as an alternative to recursion.Code the approach you are most comfortable with, usually backtracking with mutable path.Test with edge cases and explain complexity.

Time Allocation

Clarify: 2min → Approach: 3min → Code: 10min → Test: 5min. Total ~20min

What the Interviewer Tests

Interviewer tests your understanding of backtracking, recursion, string building, and handling edge cases efficiently.

Common Follow-ups

  • What if digits include '1' or '0'? → Handle by skipping or returning empty.
  • How to handle very long input? → Discuss pruning or iterative approach.
  • Can you generate combinations in lex order? → Sort or use ordered data structures.
  • What if mapping changes dynamically? → Use parameterized mapping.
💡 These follow-ups test your ability to adapt the solution to variations and constraints beyond the basic problem.
🔍
Pattern Recognition

When to Use

1) Input is a string of digits or characters; 2) Need to generate all combinations or permutations; 3) Each position has multiple choices; 4) Output all possible strings.

Signature Phrases

all possible letter combinationsmapping digits to lettersreturn all combinations

NOT This Pattern When

Problems that ask for counting combinations without generating them or greedy selection problems.

Similar Problems

Generate Parentheses - backtracking to build all valid stringsPermutations - exploring all orderings of elementsCombination Sum - backtracking to find all sums

Practice

(1/5)
1. You need to generate all combinations of balanced parentheses pairs for a given number n. Which algorithmic approach guarantees generating only valid sequences without generating invalid ones first?
easy
A. Brute force: generate all possible sequences of '(' and ')' of length 2n, then filter valid ones
B. Backtracking with pruning: build sequences by adding '(' or ')' only when it keeps the sequence valid
C. Greedy approach: always add '(' until n is reached, then add ')' to close all opened parentheses
D. Dynamic programming: count the number of valid sequences using Catalan number formula

Solution

  1. Step 1: Understand problem constraints

    We want to generate all valid parentheses sequences without generating invalid ones first.
  2. Step 2: Analyze approaches

    Brute force generates all sequences including invalid ones, greedy fails to generate all valid sequences, DP counts sequences but does not generate them. Backtracking with pruning adds '(' or ')' only when valid, thus generating only valid sequences.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Backtracking with pruning avoids invalid sequences [OK]
Hint: Backtracking prunes invalid sequences early [OK]
Common Mistakes:
  • Thinking greedy can generate all valid sequences
  • Confusing counting with generating sequences
2. What is the time complexity of the BFS approach for removing invalid parentheses from a string of length n, considering the worst case?
medium
A. O(n * 2^n) because there can be up to 2^n states and each validity check takes O(n)
B. O(n!) because permutations of removals are explored
C. O(2^n) because BFS explores all subsets of characters without repeated checks
D. O(n^2) because each level removes one character and checks validity in O(n)

Solution

  1. Step 1: Count possible states

    Each character can be either removed or kept, so up to 2^n possible strings can be generated.
  2. Step 2: Analyze per-state cost

    Each string requires an O(n) validity check, so total worst-case time is O(n * 2^n).
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    2^n states times O(n) validity checks [OK]
Hint: 2^n states times O(n) validity checks [OK]
Common Mistakes:
  • Confusing O(n^2) with BFS complexity
  • Ignoring validity check cost
  • Assuming factorial complexity
3. The following BFS code attempts to remove invalid parentheses but contains a subtle bug:
from collections import deque

def is_valid(s: str) -> bool:
    count = 0
    for c in s:
        if c == '(': count += 1
        elif c == ')':
            count -= 1
            if count < 0:
                return False
    return count == 0

def remove_invalid_parentheses_bfs(s: str):
    res = []
    visited = set([s])
    queue = deque([s])
    found = False

    while queue:
        size = len(queue)
        for _ in range(size):
            curr = queue.popleft()
            if is_valid(curr):
                res.append(curr)
                found = True
            if found:
                continue
            for i in range(len(curr)):
                # BUG: removing characters other than parentheses
                next_str = curr[:i] + curr[i+1:]
                if next_str not in visited:
                    visited.add(next_str)
                    queue.append(next_str)
    return res
What is the bug in this code?
medium
A. The found flag is reset inside the inner loop, causing premature termination
B. The is_valid function incorrectly returns True for unbalanced strings
C. The visited set is not updated correctly, causing duplicates
D. The code removes characters even if they are not '(' or ')', expanding search unnecessarily

Solution

  1. Step 1: Identify character removal condition

    The code removes characters at every index without checking if they are '(' or ')', which is incorrect.
  2. Step 2: Consequence of bug

    Removing non-parenthesis characters leads to invalid strings and unnecessary BFS states, increasing complexity and possibly missing valid results.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Only parentheses should be removed to prune search space [OK]
Hint: Only remove '(' or ')' characters during BFS [OK]
Common Mistakes:
  • Removing all characters
  • Incorrect visited updates
  • Misusing found flag
4. Suppose the Expression Add Operators problem is modified so that digits can be reused any number of times to form expressions (i.e., unlimited reuse of digits in order). Which of the following algorithmic changes correctly adapts the backtracking solution to handle this variant without infinite loops or incorrect results?
hard
A. Remove the index increment in recursive calls to allow reuse, but add a visited set to avoid infinite loops
B. Keep the index increment as is, but allow substring splits to wrap around to the start of the string
C. Modify backtracking to not increment index after choosing a digit, but limit recursion depth to n to prevent infinite loops
D. Allow index to reset to zero after reaching the end, but prune expressions longer than a fixed length to avoid infinite recursion

Solution

  1. Step 1: Understand digit reuse impact

    Allowing reuse means the index does not always increment; digits can be chosen repeatedly, risking infinite recursion.
  2. Step 2: Prevent infinite loops

    Not incrementing index requires limiting recursion depth (e.g., max length n) to avoid infinite loops while exploring repeated digits.
  3. Step 3: Evaluate options

    Modify backtracking to not increment index after choosing a digit, but limit recursion depth to n to prevent infinite loops correctly limits recursion depth while allowing reuse; others either cause infinite loops or incorrect pruning.
  4. Final Answer:

    Option C -> Option C
  5. Quick Check:

    Limiting recursion depth prevents infinite loops with reuse [OK]
Hint: Limit recursion depth when digits can be reused [OK]
Common Mistakes:
  • Forgetting to limit recursion depth causes infinite loops
5. Suppose the Word Search problem is modified so that the same cell can be used multiple times in forming the word. Which modification to the backtracking algorithm correctly handles this change?
hard
A. Add a global visited set to track cells used in all paths
B. Keep marking visited cells but reset them after each recursion
C. Remove the in-place marking of visited cells to allow reuse
D. Use dynamic programming to store partial results and avoid recomputation

Solution

  1. Step 1: Understand new constraint

    Cells can be reused multiple times, so marking visited cells to prevent reuse is no longer needed.
  2. Step 2: Modify algorithm accordingly

    Removing in-place marking allows revisiting cells, correctly reflecting the relaxed constraint.
  3. Step 3: Evaluate other options

    Keeping marking or using global visited set prevents reuse incorrectly; DP is not suitable here.
  4. Final Answer:

    Option C -> Option C
  5. Quick Check:

    Removing visited marking enables cell reuse as required [OK]
Hint: Relaxing constraints often means removing pruning steps [OK]
Common Mistakes:
  • Keeping visited marking, causing false negatives
  • Adding unnecessary global state
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard