一、题目
二、分析
- 看到所有可能的***,一般都采用递归+回溯。
- 首先画出来分析树,因为是将 s 分割成一些子串,所以可以按照每次划分几个字符为树节点分叉。图中的
-
代表划分的分界线。
可以看到需要维护一个vector: path
来记录每一步已经划分完的、且为回文串的子串,请仔细阅读这句话。还需要维护一个划分起始点startCut
。
- 根据上图,可以知道当划分起始点为原字符串长度时,终止递归,将path拷贝到result中。所以有了以下代码:
if(startCut == len){
result.push_back(path);
return;
}
- 每进入一个树结点的子节点,都要判断即将划分的子串是否是回文串,如果不是,直接跳过,比如图中的
a - ab -
。
判断子串是否为回文串的函数就不单独写了。 - 从第一层可以看到,有了划分起始点
startCut
之后,每次需要截取多长的子串,是需要遍历的。(例如,startCut = 0
时,分别截取1、2、3个字符)所以需要加一个for循环。
子树第一层:startCut = 0: 截取1, 2, 3. 对应了根节点的3个分支
子树第二层:startCut = 1: 截取1, 2. 对应了最左侧子树的两个分支
子树第三层:startCut = 2: 截取1
----- 代码:
for(int i = startCut; i < len; i ++){
// 截取子串 startCut 开始的 i-startCut + 1个字符。
string substr = s.substr(startCut,i-startCut+1);
if(substr == "" || !isnotH(substr)) continue;
path.push_back(substr);
// 每次截取之后,都要从截取到的地方作为新的 划分起始点。比如从0开始截了2字符,那下次的startCut就是2。
DFSback(s, len, 1 + i);
path.pop_back();
}
三、代码
class Solution {
private:
vector<vector<string>> result;
vector<string> path;
public:
vector<vector<string>> partition(string s) {
DFSback(s, s.length(), 0);
return result;
}
// 回溯
void DFSback(string s, int len, int startCut){
if(startCut == len){
// 终止递归
result.push_back(path);
return;
}
for(int i = startCut; i < len; i ++){
string substr = s.substr(startCut,i-startCut+1);
if(substr == "" || !isnotH(substr)) continue;
path.push_back(substr);
DFSback(s, len, 1 + i);
path.pop_back();
}
}
// 是否为回文串
bool isnotH(string str){
if(str.length() == 1) return true;
for(int i = 0; i < str.length()/2; i ++){
if(str[i] != str[str.length() - i - 1]) return false;
}
return true;
}
};
执行用时:192 ms, 在所有 C++ 提交中击败了48.77%的用户
内存消耗:78 MB, 在所有 C++ 提交中击败了32.18%的用户