目录
一、简介
回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。回溯法很有趣可以解决一系列的问题,比如子集问题、排列问题以及八皇后问题等。是个很值得研究和学习的算法。
二、题目
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。返回 s 所有可能的分割方案。
示例:
输入: "aab"
输出:["aa","b"]和 ["a","a","b"]
三、实战
1、判断一个字符串是否是回文串
这个可以暴力求解
bool isPalindrome(std::string str2, int start, int end)
{
const char*str = str2.c_str();
int ns = end - start;
ns = ns / 2;
for (int i = 0; i < ns; ++i)
{
if (str[i+start] != str[end - i - 1])
{
return false;
}
}
return true;
}
2、回溯法实现
回溯法其实我感觉就是在一颗树上进行深度优先搜索(dfs)。这棵树怎么组织是根据不同题目的,比如本文的题目我把它的树的结构画出来了。dfs遍历这棵树就可以,然后通过条件进行一些剪枝,最后把需要的结果保存起来即可。
图 1
void Palindrome(std::string &str, int start, int end, std::vector<std::string> &vecstr, std::vector<std::vector<std::string>> &vecres)
{
int n = str.size();
if (start == end)
{
vecres.push_back(vecstr);
return;
}
for (int i = start; i < end; ++i)
{
if (isPalindrome(str, start, i+1))
{
vecstr.push_back(str.substr(start, i+1-start));
Palindrome(str, i + 1, end, vecstr,vecres);
vecstr.pop_back();
}
}
}
vecstr用于记录dfs路径上的节点,符号条件的都会搜索到最后start==end,不符号条件的不会进行到最后。比如{}->a->a->b->end,这条路径会执行到start==end。而{}->a->ab这个路径是不行的,ab它不是回文串,不会执行到最后的start==end。 vecres用于记录结果,符号条件的结果存到vecres即可。isPalindrome就是用来剪枝的。
3、效率优化
isPalindrome调用次数太多,可以用一个二维数组dp[i][j]来表示第j位置到第i位置的字符串是否是回文串。我这里使用了一点动态规划去求解dp数组,其实也可以双重循环暴力求解dp数组。
dp状态方程
dp[i][j] = dp[i-1][j+1] : str[i]==str[j]
dp[i][j]=true :str[i]==str[j] && (i-j)<=2
for (int i = 0; i < pstr.length(); ++i)
{
for (int j = 0; j <= i; ++j)
{
if (str[j] == str[i] && ((i - j) <= 2 || dp[i - 1][j + 1]))
{
dp[i][j] = true;
}
}
}
void Palindrome_dp(std::string &str, int start, int end, std::vector<std::string> &vecstr, std::vector<std::vector<std::string>> &vecres,bool** pdp)
{
int n = str.size();
if (start == end)
{
vecres.push_back(vecstr);
return;
}
for (int i = start; i < end; ++i)
{
//if (isPalindrome(str, start, i + 1))
if (pdp[i][start])
{
vecstr.push_back(str.substr(start, i + 1 - start));
Palindrome(str, i + 1, end, vecstr, vecres);
vecstr.pop_back();
}
}
}
四、总结
算法都会对应一个数据结构,把算法对应的数据结构弄明白了,问题就easy了。回溯法就是对应这一颗树,然后在这棵树上进行dfs,再根据题目进行剪枝,在dfs过程中把结果保存。底下我会写一系列的关于回溯法的文章。