子集型回溯
回溯过程:
当前操作——枚举path字符串第idx位字符
子问题——构造字符串>=idx(idx<=dights.length())的部分,idx为dights的索引
下一个子问题——构造字符串>=idx+1(idx<=dights.length())的部分
方向——dfs(idx)=>dfs(idx+1)
class Solution {
public:
string path;
vector<string> ans;
string phoneMap[10] = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
//创建phoneMap,对应数字键上的字母
vector<string> letterCombinations(string digits) {
if(digits.length() == 0) return ans;//说明按了0或1,直接返回
dfs(0, digits);//回溯要用到枚举path当前状态idx
return ans;
}
void dfs(int idx, string digits) {
if(idx == digits.length()){//idx达到digits的长度说明一次构造完成,作为递归出口
ans.push_back(path);//将path路径上得的解存进结果ans
return;
}
int digit = digits[idx] - '0';//枚举当前数字digit
string letters = phoneMap[digit];//从phoneMap中获取digit数字键对应的letters字母串
for(char &letter: letters) {//遍历letters字母串,构造字符串>=idx
path.push_back(letter);//将已构造部分加进path路径
dfs(idx + 1, digits);//遍历letters字母串,构造字符串>=idx+1
path.pop_back();//恢复上一步现场
}
}
};
法一回溯过程:
当前操作——枚举nums数组第idx位数字选/不选
子问题——从数组>=idx(idx<=nums.size())的部分构造子集,idx为nums的索引
下一个子问题——从数组>=idx+1(idx<=nums.size())的部分构造子集
方向——dfs(idx)=>dfs(idx+1)
class Solution {//法一:选或不选(输入视角)
public:
vector<int> path;
vector<vector<int>> ans;
vector<vector<int>> subsets(vector<int>& nums) {
dfs(0, nums);
return ans;
}
void dfs(int idx, vector<int>& nums) {
if(idx == nums.size()) {
ans.push_back(path);
return;
}
dfs(idx + 1, nums);//不选
path.push_back(nums[idx]);//选
dfs(idx + 1, nums);
path.pop_back();//恢复上一步现场
}
};
法二回溯过程:
当前操作——枚举nums数组第idx位数字,加入path
子问题——从数组下标i>=idx(idx<=nums.size())的数字中构造子集,idx为nums的索引
下一个子问题——从数组下标>=i+1(idx<=nums.size())的数字中构造子集
方向——dfs(idx)=>dfs(i+1)、dfs(i+2)、dfs(i+3)、···、dfs(i+nums.size())
应注意:必选一个情况下应避免类似于[1,2]和[2,1]情况,我这里规定下标严格递增
class Solution {//法二:选哪个(答案视角)
public:
vector<int> path;
vector<vector<int>> ans;
vector<vector<int>> subsets(vector<int>& nums) {
dfs(0, nums);
return ans;
}
void dfs(int idx, vector<int>& nums) {
ans.push_back(path);
for(int i = idx; i < nums.size(); i++) {
path.push_back(nums[i]);
dfs(i + 1, nums);
path.pop_back();
}
}
};
法一回溯过程:
当前操作——
class Solution {//法一:转换为逗号选或不选(输入视角)
public:
vector<string> path;
vector<vector<string>> ans;
vector<vector<string>> partition(string s) {
dfs(0, 0, s);
return ans;
}
bool check(string &s, int left, int right) {//判断回文串
while(left < right)
if(s[left++] != s[right--])
return false;
return true;
}
void dfs(int idx, int start, string &s) {
int n = s.length();
if(idx == n) {
ans.push_back(path);
return;
}
if(idx < n-1) dfs(idx + 1, start, s);//不选
if(check(s, start, idx)) {//选
path.push_back(s.substr(start, idx - start + 1));
dfs(idx + 1, idx + 1, s);
path.pop_back();
}
}
};
法二回溯过程:
class Solution {//法二:枚举结束位置(答案视角)
public:
vector<string> path;
vector<vector<string>> ans;
vector<vector<string>> partition(string s) {
dfs(0, s);
return ans;
}
bool check(string &s, int left, int right) {
while(left < right)
if(s[left++] != s[right--])
return false;
return true;
}
void dfs(int idx, string &s) {
int n = s.length();
if(idx == n) {
ans.push_back(path);
return;
}
for(int i = idx; i < n; i++) {
if(check(s, idx, i)) {
path.push_back(s.substr(idx, i - idx + 1));
dfs(i + 1, s);
path.pop_back();
}
}
}
};
法一回溯过程:
当前操作——枚举s字符串每个字母的变化后情况,加入path
子问题——从path字符串>=i的部分构造子集
下一个子问题——从path字符串>=j+1的部分构造子集
方向——dfs(i)=>dfs(j+1)、dfs(j+2)、dfs(j+3)、···、dfs(j+s.length())
class Solution {//法一:每次操作都变(答案视角)
public:
string path;
vector<string> ans;
vector<string> letterCasePermutation(string s) {
path = s;
dfs(0, s);
return ans;
}
void dfs(int i, string &s) {
ans.push_back(path);//每次都选择变,把答案加进ans
if(i == s.length()) {
return;
}
for(int j = i; j < s.length(); j++) {
if(s[j] >= 'a' && s[j] <= 'z') {
path[j] = s[j]-'a'+'A';
}
else if(s[j] >= 'A' && s[j] <= 'Z') {
path[j] = s[j]-'A'+'a';
}
else continue;//遇到数字直接退出,不能走dfs
dfs(j + 1, s);
if(s[j] >= 'a' && s[j] <= 'z') {
path[j] = path[j]-'A'+'a';//+=32也行
}
else if(s[j] >= 'A' && s[j] <= 'Z') {
path[j] = path[j]-'a'+'A';//-=32也行
}
//恢复上一个操作
}
}
};
法二回溯过程:
当前操作——枚举path第i位字母变/不变
子问题——从数组>=i 的部分构造子集,i为path的索引
下一个子问题——从数组>=i+1 的部分构造子集
方向——dfs(i)=>dfs(i+1)
class Solution {//法二:枚举字母变或不变(输入视角)
public:
string path;
vector<string> ans;
vector<string> letterCasePermutation(string s) {
path = s;
dfs(0, s);
return ans;
}
void dfs(int i, string &s) {
if(i == s.length()) {
ans.push_back(path);
return;
}
path[i] = s[i];
dfs(i + 1, s);
if(s[i] >= 'a' && s[i] <= 'z') {
path[i] = s[i]-'a'+'A';
}
else if(s[i] >= 'A' && s[i] <= 'Z') {
path[i] = s[i]-'A'+'a';
}
else return;
dfs(i + 1, s);
}
};
回溯过程:
class Solution {
public:
int punishmentNumber(int n) {
int ans = 0;
for(int i = 1 ;i <= n; i++) {
if(check(i)) {
ans += i*i;
}
}
return ans;
}
bool check(int n) {
if(n == 1) return true;
string s = to_string(n*n);
return dfs(0, n, s, 0);
}
bool dfs(int i, int &n, string &s, int sum) {
if(sum == n && i == s.size()) {
return true;
}
for(int j = i; j < s.size(); j++) {
int cur = stoi(s.substr(i, j-i+1));
sum += cur;
if(sum > n) break;
if(dfs(j+1, n, s, sum)) return true;
sum -= cur;
}
return false;
}
};
未解决(太难了):
306. 累加数
1601. 最多可达成的换楼请求数目