分割回文串
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
回文串 是正着读和反着读都一样的字符串。
示例 1:
输入:s = “aab”
输出:[[“a”,“a”,“b”],[“aa”,“b”]]
示例 2:
输入:s = “a”
输出:[[“a”]]
思考
本题这涉及到两个关键问题:
- 切割问题,有不同的切割方式
- 判断回文
相信这里不同的切割方式可以搞懵很多同学了。
这种题目,想用for循环暴力解法,可能都不那么容易写出来,所以要换一种暴力的方式,就是回溯。
一些同学可能想不清楚 回溯究竟是如何切割字符串呢?
我们来分析一下切割,其实切割问题类似组合问题。
例如对于字符串abcdef:
- 组合问题:选取一个a之后,在bcdef中再去选取第二个,选取b之后在cdef中在选组第三个…。
- 切割问题:切割一个a之后,在bcdef中再去切割第二段,切割b之后在cdef中在切割第三段…。
感受出来了不?
所以切割问题,也可以抽象为一棵树形结构,如图:
递归用来纵向遍历,for循环用来横向遍历,切割线startIndex切割到字符串的结尾位置,说明找到了一个切割方法。
此时可以发现,切割问题的回溯搜索的过程和组合问题的回溯搜索的过程是差不多的。
#回溯三部曲
- 递归函数参数
全局变量数组path存放切割后回文的子串,二维数组lists存放结果集。 (这两个参数可以放到函数参数里)
本题递归函数参数还需要startIndex,因为切割过的地方,不能重复切割,和组合问题也是保持一致的。
代码如下:
List<List<String>> lists =new ArrayList<>();
List<String> path = new ArrayList<>();
int startIndex = 0;
- 递归函数终止条件
从树形结构的图中可以看出:切割线切到了字符串最后面,说明找到了一种切割方法,此时就是本层递归的终止终止条件。
if(startIndex==s.length()){
lists.add(new ArrayList<>(path));
return;
}
那么在代码里什么是切割线呢?
在处理组合问题的时候,递归参数需要传入startIndex,表示下一轮递归遍历的起始位置,这个startIndex就是切割线。
- 单层搜索的逻辑
来看看在递归循环,中如何截取子串呢?
在for (int i = startIndex; i < s.size(); i++)
循环中,我们 定义了起始位置startIndex,那么 [startIndex, i] 就是要截取的子串。
首先判断这个子串是不是回文,如果是回文,就加入在List<string> path
中,path用来记录切割过的回文子串。
// 是截取startIndex 到 i+1 的字符串 判断该字符串是否为回文字符串
for (int i = startIndex; i < s.length();i++){
if(isHUIWen(s,startIndex,i)){
path.add(s.substring(startIndex,i+1));
}
else {
continue;
}
backTracking(lists,path,i+1,s);
path.remove(path.size()-1);
}
注意切割过的位置,不能重复切割,所以,backtracking(s, i + 1); 传入下一层的起始位置为i + 1。
#判断回文子串
最后我们看一下回文子串要如何判断了,判断一个字符串是否是回文。
可以使用双指针法,一个指针从前向后,一个指针从后先前,如果前后指针所指向的元素是相等的,就是回文字符串了。
public boolean isHUIWen(String s,int begin,int end){
while (begin<end){
if(s.charAt(begin)!=s.charAt(end)){
return false;
}
begin++;
end--;
}
return true;
}
完整代码
class Solution {
public List<List<String>> partition(String s) {
List<List<String>> lists =new ArrayList<>();
List<String> path = new ArrayList<>();
int startIndex = 0;
backTracking(lists,path,startIndex,s);
return lists;
}
private void backTracking(List<List<String>> lists, List<String> path, int startIndex,String s) {
if(startIndex==s.length()){
lists.add(new ArrayList<>(path));
return;
}
// 是截取startIndex - i+1 的字符串 判断该字符串是否为回文字符串
for (int i = startIndex; i < s.length();i++){
if(isHUIWen(s,startIndex,i)){
path.add(s.substring(startIndex,i+1));
}
else {
continue;
}
backTracking(lists,path,i+1,s);
path.remove(path.size()-1);
}
}
public boolean isHUIWen(String s,int begin,int end){
while (begin<end){
if(s.charAt(begin)!=s.charAt(end)){
return false;
}
begin++;
end--;
}
return true;
}
}