在做过了前面几道深搜的题目后,慢慢地会了深搜的思考方式。这里做出总结,可以看出题目都有一些共性。还没做过题目的可以看看这些例题:
做了以上这些题目后,我们可以总结出,深搜往往是解决这样的问题:求满足条件的所有可行解,判断已知的一条路径是否符合条件。
再往细分,可以看成一下几类:
- 给一个数组,求满足条件的所有可行解。涉及到基本的排列组合问题,若要求去除重复,也要会去重。
- 类似走迷宫,给一个路径,判断是否存在。与上面基本的排列组合,只是状态扩展的问题
- 给一个字符串,求所有满足条件的分割方式。这里的这个条件很灵活,比如按照IP地址的规则,按照括号匹配的规则,按照回文串的规则分割等等。与对数组的深搜相比,对字符串处理难一些。我们处理的常用思想是:设置一个start变量,表示我们当前处理的字符串是从start位置到末尾的,这样当start==n是,即表示所有可能已处理完毕
举一些对字符串分割的例子,分析一下:
关键是要理清楚深搜的过程,找出条件,最后转化为代码。
对于括号问题:第一个必须是(,第二个可以是(或),那什么时候可以放)?必须是左括号数量比右括号多的时候,自然要定义2者的数量l,r;当l==n时,左括号全放完了,只能用右括号了,便结束了,代码自然就写出来了
恢复IP地址:按照上面的总结,应该设置个start变量。IP地址只有4部分,还需要个step变量记录到了哪一部分,只有start到了最后且step到了4才算找到一组
用图来表示;
恢复IP地址的代码:
class Solution {
public:
/*
* @param s: the IP string
* @return: All possible valid IP addresses
*/
vector<string> restoreIpAddresses(string &s) {
// write your code here
vector<string> res;
string tmp;
DFS(s,res,tmp,0,0);
return res;
}
void DFS(string& s,vector<string>& res,string tmp,int start,int step){
//收敛条件
if(start==s.size() && step==4){
tmp.resize(tmp.size()-1);//去除最后一个.
res.push_back(tmp);
return;
}
//根据长度进行剪枝
if(s.size()-start>(4-step)*3) return;
if(s.size()-start<(4-step)) return;
int num=0;
//状态的扩展
for(int i=start;i<start+3;++i){
num=num*10+(s[i]-'0');
if(num<=255){//合法
tmp+=s[i];
DFS(s,res,tmp+'.',i+1,step+1);
}
if(num==0) break;
}
}
};
总结一下深搜的代码模板:
void dfs(已知数据,最终结果result,当前路径path,.....) {
if (cur == input.size()) { // 收敛条件
// 找到了一个可行解
将 path 放入 result
}
if (可以剪枝) return;
for(...) { // 执行所有可能的扩展动作
执行动作,修改 path
递归调用
恢复 path
}
}