题目一
力扣:131
思路
1、要先弄懂本题目是啥意思,要对s进行分割,分割出来的各个部分必须要是回文的,要求出所有可能的组合,这个一听,求出所有可能的情况,这个时候就要想到,可能是回溯算法,也有可能是DP,再看,是字符串分割问题,这种问题一般就是定位到回溯法暴力枚举所有可能的结果,用一些条件进行剪枝来优化。
2、到了这里我们来分析一下什么时候用回溯:
组合问题:N个数里面按一定规则找出k个数的集合
排列问题:N个数按一定规则全排列,有几种排列方式
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
棋盘问题:N皇后,解数独等等
3、回溯问题通用的一个模板:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
4、综上,本体可以确定了,用回溯算法,回溯算法,最简单,最有效,最清晰的分析方法就是,画一棵树来分析:
所以当分析到了某个点i的时候,就要枚举从他开始的后面所有点,因为每一处都可能是分割点,如果能分割就递归走下去,分割条件就是是回文串,这也同样起到了剪枝的效果。
5、因为每一次都要回文串判断,所以我们想能不能在最短时间内判断是不是回文串,可以,用unordered_set,那前提得把所有可能的回文串存进去,所以我们要预处理,存进去所有回文串,用DP,这个就略过了,介绍过很多遍了。
代码
注意:
1、这里用的是全局量维护某一个答案,所以在回溯回来之后,说明刚才哪个分支已经完事了,所以要pop_back出去
class Solution {
public:
vector<vector<string>> ans;//答案
vector<string> tmp;//临时
bool dp[100][100] = {};//DP
unordered_set<string> item;//hash,快速判断回文串
int n = 0;
string s;
void getArrange(int i) {//回溯法
if (i == n) {//走到尽头,说明此分割有效
ans.push_back(tmp);
return;
}
for (int j = i; j < n; j++) {//枚举身后的,要从i开始,包括自己本身
if (item.count(s.substr(i, j - i + 1))) {
tmp.push_back(s.substr(i, j - i + 1));//加入临时答案
getArrange(j + 1);//说明此次i-j合法,从j处分割,之后的进入递归
tmp.pop_back();//分支完事,弹出,恢复现场
}
}
}
vector<vector<string>> partition(string s) {
n = s.size();
this->s = s;
for (int i = 0; i < n; i++) {//DP预处理,初始化
dp[i][i] = true;
item.insert(s.substr(i, 1));
}
for (int i = 0; i < n - 1; i++) {//初始化
if (s[i] == s[i + 1]) {
dp[i][i + 1] = true;
item.insert(s.substr(i, 2));
}
}
for (int l = 2; l < n; l++) {
for (int i = 0, j; i + l < n; i++) {
j = i + l;
if (s[i] == s[j] && dp[i + 1][j - 1]) {
dp[i][j] = true;
item.insert(s.substr(i, l + 1));
}
}
}
getArrange(0);
return ans;
}
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):