回溯的关键点:递归函数的参数设计,递归之前push_back,递归之后pop_back。
1) Word Break II
首先使用动态规划计算出子串s[i]...s[j]是否能够被分割成多个单词。(见Work Break)
然后递归的创建句子。递归函数的参数有当前处理的s的开头下标begin,当前已经选择了的单词的集合temp,最终结果的集合result。
当begin到达s的末尾时,说明已经找到了一种合理的分割方式,那么将当前已经选择了的全部单词(在temp中)组成句子,将句子加入到result中。
当begin还没到达s的末尾时,枚举从begin开始,下一次分割的位置k,如果这个分割使得从s[i]到s[k]构成一个单词,同时从s[k+1]往后的部分也能被成功分割。那么,这就是一种可能的选择,将这个单词加入到temp,递归的进入分割从s[k+1]到s末尾的工作。当递归结束之后,应该讲当前这个单词从temp中删除,回溯的寻找其他的结果。
2) Palindrome Partition
首先使用动态规划计算出子串s[i]...s[j]是否是回文。
然后递归的创建分割。递归函数的参数有当前处理的s的开头下标begin,当前已经分割得到的回文集合temp,最终结果的集合result。
当begin到达s的末尾时,说明已经找到了一种合理的分割方式,那么将当前已经得到的全部回文单词集合temp加入到result中。
当begin还没到达s的末尾时,枚举从begin开始,下一次分割的位置k,如果这个分割使得从s[i]到s[k]构成一个回文,则递归的进入分割s[k+1]到s末尾的工作。
3) Restore IP Addresses
递归函数参数包括当前处理的string下标begin,当前这次递归得到的IP分割中间结果temp,已经得到的有效IP的结果集合result。
如果当前处理的下标begin已经等于s.length(),判断当前得到的中间结果temp是否是分割成四段。如果不是,直接返回,说明不是有效的IP分割。如果是,则判断每一段是否有前导0,如果没有则是有效IP分割,放到result中。
如果begin没到达s.length()。枚举下次分割的位置,计算以此位置分割得到的数值,如果数值超过255则返回,否则将这次的分割记录到temp中,递归的分割下一部分。回溯是指,当递归结束后,将当前的分割从temp中删除,尝试将分割的位置后移一位。
4) Subsets I II
递归函数参数包括当前处理的数组下标begin,当前得到的子集temp,当前得到的子集集合result。
如果begin等于n,则当前得到一个新的子集,加入到result中。
如果begin不等于n,则枚举下一个要加入子集的元素下标,将该元素加入temp,递归的处理后面的数组。回溯时将该元素从temp中删除,继续枚举下一个元素。
注意,当枚举的元素下标到达n时,应当额外的写一条语句,将当前的temp加入到result中。
void build(vector<int> &S, int begin, vector<int> temp, vector<vector<int>> &result)
{
int n = S.size();
if(begin == n)
{
result.push_back(temp);
return;
}
int i;
for(i=begin;i<n;i++)
{
temp.push_back(S[i]);// pick S[i]
build(S, i+1, temp, result);// solve sub-problem recursively
temp.pop_back();// delete S[i]
}
// when i=n-1 and S[n-1] is pop_back, i++, the cycle is stoped
// then, the corresponding subset is not pushed into result
// so push it
result.push_back(temp);