When Depth First Search (DFS) encounters the problem of alphanumeric combinations [DFS, backtracking method]

When depth-first search (DFS) encounters alphanumeric problems

Problem Description

Given a string containing only the numbers 2-9, you need to return all the letter combinations it can represent. Each number can represent any combination of letters in the corresponding number. Note that 1 does not correspond to any letter.

Here are examples of questions:

Example 1:

Input: digits = "23"
Output:["ad","ae","af","bd","be","bf","cd","ce","cf"]

Example 2:

Input: digits = ""
Output:[]

Example 3:

Input: digits = "2"
Output:["a","b","c"]

Solve thoughts

To solve this problem, we can use the depth-first search (DFS) method to process each digit of the input number one by one from beginning to end to generate all possible letter combinations. We will maintain a mapping table to save the correspondence between numbers and letters, and then use recursion to explore all possible combinations.

Thought process

  1. Create a mapping table: We first need to create a mapping table to correspond to the set of numbers and letters. This will help us know which letters to choose from as we work with each number. std::mapThis mapping table can be implemented using an or array.

  2. Recursively generate combinations: We will write a recursive function to handle the process of generating combinations of letters. This function will take several parameters: the current numeric index, the currently constructed string, and the result set. We start at index 0 and process each number recursively.

  3. Recursive Termination Condition: Inside the recursive function, we first check if the current numeric index is equal to the length of the input string. If so, we've processed all the numbers, added the currently constructed string to the result set, and returned.

  4. Process the current number: If the current index does not reach the termination condition, we need to obtain the set of letters corresponding to the current number. Then, for each letter, we add it to the end of the currently constructed string and call the function recursively, processing the next number (increasing index).

  5. Get the letter set: We can get the letter set corresponding to the current number through the previously established mapping table. When iterating through the collection of letters, each time the selected letter is added to the currently constructed string and then the next number is processed recursively.

  6. Result set merging: During the recursive process, strings will be continuously constructed until all numbers have been traversed. This way, all possible letter combinations will be included in the final result set.

Code:

class Solution {
    
    
public:
    map<char , string> digitToLetters = {
    
    
        {
    
    '2', "abc"},
        {
    
    '3', "def"},
        {
    
    '4', "ghi"},
        {
    
    '5', "jkl"},
        {
    
    '6', "mno"},
        {
    
    '7', "pqrs"},
        {
    
    '8', "tuv"},
        {
    
    '9', "wxyz"}
    };

    void generateCombinations(string digits , int index , string current , vector<string> &ans){
    
    
        if(index == digits.length()){
    
    
            ans.push_back(current);
            return ;
        }

        char now = digits[index];
        string nowStr = digitToLetters[now];

        for(char a : nowStr){
    
    
            generateCombinations(digits , index + 1 , current + a , ans);
        }

    }

    vector<string> letterCombinations(string digits) {
    
    
        vector<string> ans;

        if(digits.empty()){
    
    
            return ans;
        }

        generateCombinations(digits , 0 , "" , ans);

        return ans;
    };
};

Potentially confusing questions

1. Why use depth-first search?

Depth-first search is a recursive algorithm that searches for all possible solutions in the state space of a problem. In this problem, we need to process each number from left to right, selecting possible letters one by one, and then continue processing the following numbers in the next level of recursion. Depth-first search is well suited to solve combinatorial problems that require step-by-step selection.

2. How to deal with empty input?

In the case where the input is empty, we want to return an empty result set. At the beginning of the function we can handle this case by checking if the input length is zero and directly return an empty result set in this case.

3. Why can we use index == digits.length()conditions as recursive exits?

The reason for using a conditional index == digits.length()as the recursive exit is because we are dealing with digits.length() - 1numeric indices from 0 to , which represents every number in the string.

During a depth-first search, we first process the number with index 0, then recursively process the number with index 1, and so on. When we reach an index equal to the length of the string, it means we have processed all the numbers. Considering that string indexing starts from 0, if the index is equal to the length of the string, it means that we have processed all the numbers in the entire string.

The benefit of using index == digits.length()it as an exit from the recursion is that it ensures that we step through each number during the recursion and terminate the recursion after the last number has been processed. This way we are able to generate all possible letter combinations and build the correct string at each recursive step.

4. Difference from backtracking method
Backtracking method and DFS are almost equivalent in this problem, because their core ideas are deep search. In actual operation, the backtracking method may perform some pruning optimization during selection to reduce unnecessary searches.

  • The implementation of the backtracking method further embodies 回溯the idea of ​​" " in function naming;
  • And DFS is传统的深度优先搜索

In terms of code implementation, their logic is almost the same, both traversing combinations of numbers and letters through recursion.

The backtracking method is usually used for more complex combinatorial problems, where conditional judgments are required when selecting paths, while DFS is more general and suitable for various types of deep search problems. In this particular question, both are used interchangeably.

Guess you like

Origin blog.csdn.net/qq_22841387/article/details/132196670