题目描述
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例:
输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number
题解
本题是非常典型的回溯法。(这是时隔几个月没刷算法题之后再次打开leetcode,也是第一次尝试做回溯法,理解有问题的话有怪莫怪哈)
首先,这一道题是一个很典型的全排列问题,最后要输出的结果是穷举得到的,所以时间复杂度和空间复杂度是指数级的,一般人不太可能想到简化的办法。
在学习回溯法之前看到这道题是完全懵逼的,似乎只能想到一个又一个的for循环反复嵌套。但是其实一般人到这里,在稍微思考一下就可以看出,无数简单计算的嵌套其实就是递归,所以这样的穷举问题肯定是可以由递归来实现的。所以回溯法其实也只是借用函数递归来解决这样的穷举问题,并没有时间复杂度上面的优势。
首先来看一个随便百度一下就能找到的关于回溯算法的套路:
backtrace(路径,选择列表)
if 可以结束:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrace(路径,选择列表)
撤销选择
回溯法,顾名思义,也就是在特定的条件下(譬如已经遍历了子树上的所有节点),那么就要返回到最开始出发的位置,放在网上各种资料有关于回溯算法的总结中,也就是指的“撤销选择”这一步骤。
代码
class Solution {
private:
string str[8]={"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public:
vector<string> ret;
vector<string> letterCombinations(string digits)
{
if(digits.empty()) return ret;
string trace="";
backtrack(trace,digits);
return ret;
}
void backtrack(string trace,string nums)
{
vector<string> nums8(str,str+8);
if(nums.empty()) //可以结束
{
ret.push_back(trace);
return;
}
int n=nums[0]-'0'-2;
nums.erase(nums.begin());
for(int i=0;i<nums8[n].size();i++)
{
trace=trace+nums8[n][i];//做选择
backtrack(trace,nums);
trace.pop_back();//撤销选择
}
}
};