本文目录
1 基本概念
回溯算法(试探算法)是一种按指定策略遍历所有可能路径的方法,在遍历的过程中,找出所有满足条件的解。每走一步,判断当前情况是否满足条件,若满足条件,则在该路径上继续试探,若不满足条件,则回溯(回退)至上一步,然后换一条路径继续试探。
2 分割回文串
例题:给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。返回 s 所有可能的分割方案。
出自:中文版 LeetCode 第 131 题 - 分割回文串 问题
思路:
先寻找第一个满足条件的子字符串,记录结束位置为 endIndex1;再从 endIndex1 + 1 位置开始,寻找第二个满足条件的子字符串,记录结束位置为 endIndex2;以此类推,当最后一个满足条件的子字符串结束位置正好在原字符串 s 结尾处,则表示正好将原字符串 s 分割为多个回文子串,即该分割方案是满足条件的一个解。
class Solution {
private boolean[] indexFlag;
private List<List<String>> resultList;
public List<List<String>> partition(String s) {
indexFlag = new boolean[s.length()]; // 记录分割点
resultList = new ArrayList<>();
findSubPalStr(s, 0); // 从0下标点开始遍历
return resultList;
}
/**
* 回溯算法:从指定位置开始寻找下一个回文子串
*/
private void findSubPalStr(String s, int startIndex) {
for (int endIndex = startIndex; endIndex < s.length(); endIndex++) {
if (isPalindrome(s, startIndex, endIndex)) { // 判断当前子串是否为回文(当前步骤是否满足条件)
indexFlag[endIndex] = true; // 记录分割点
if (!isSolution(s, endIndex)) { // 判断是否为题解
findSubPalStr(s, endIndex + 1); // 非题解:进行下一步尝试
}
indexFlag[endIndex] = false; // 抹除记录:回溯至上一步状态
}
}
}
/**
* 当前步骤判断:是否为回文串
*/
private boolean isPalindrome(String s, int leftIndex, int rightIndex) {
while (leftIndex <= rightIndex && s.charAt(leftIndex) == s.charAt(rightIndex)) {
leftIndex++;
rightIndex--;
}
return leftIndex >= rightIndex;
}
/**
* 整体方案判断:是否将原字符串 s 正好分割为多个回文子串,即 当前方案是否为题解。
*/
private boolean isSolution(String s, int currentIndex) {
// 判断条件:当前位置正好在原字符串 s 结尾处,表示正好将 原字符串 s 分割为多个回文子串。
if (currentIndex == s.length() - 1) {
List<String> resultItem = new ArrayList<>();
// 遍历分割点 并 构造结果项
for (int start = 0, end = 0; end < indexFlag.length; end++) {
if (indexFlag[end]) {
resultItem.add(s.substring(start, end + 1));
start = end + 1;
}
}
resultList.add(resultItem);
return true;
}
return false;
}
}
扩展内容:
由于该题需要找到所有满足条件的方案,因此,使用回溯算法进行求解。
若该题只需要判断是否存在满足条件的方案,则可以使用动态规划进行求解,其思路可参考《动态规划:单词拆分》。