字符串的分割方案(LC的第131题)
题目描述:给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
示例: 输入: "aab" 输出: [ ["aa","b"], ["a","a","b"] ]
思路:这道题的难点在于如何切割,由于要求出所有可能的切割方案,因此要用到穷举法,但是仅仅用for循环极难实现(不知道字符串的长度,回文子串的长度也未知)。回溯法天然的可以遍历到任意长度的子串,在对其进行判断,在这里借用Carl代码随想录的图,具体过程如下:
我们可以看到在横向遍历中,变化的一直是要截取的子串的终止位置,而纵向遍历变的却是要截取字串的起始位置,因此当算法结束可以访问到字符串的所有子串。核心代码如下:
private void backTracking(String s, int startIndex) {
//如果起始位置大于s的大小,说明找到了一组分割方案
if (startIndex >= s.length()) {
lists.add(new ArrayList(deque));
return;
}
for (int i = startIndex; i < s.length(); i++) {
//如果是回文子串,则记录
if (isPalindrome(s, startIndex, i)) {
String str = s.substring(startIndex, i + 1);
deque.addLast(str);
} else {
continue;
}
//起始位置后移,保证不重复
backTracking(s, i + 1);
deque.removeLast();
}
}
//判断是否是回文串
private boolean isPalindrome(String s, int startIndex, int end) {
for (int i = startIndex, j = end; i < j; i++, j--) {
if (s.charAt(i) != s.charAt(j)) {
return false;
}
}
return true;
}
总结:在回溯算法的切割问题中,一定要注意切割的位置,递归的终止条件,横向遍历和纵向遍历的所代表的含义,在本题中,树的一条分支,即纵向遍历是一中切割方案,在一个集合中,横向遍历代表的是不同的切割方案,在不同的集合中。
切割问题的关键在于如何切割,确定好切割点就问题就容易很多,其他的就是一些条件判断和优化方面的问题,例如复原IP地址(LC的第93题),这题的切割方案与上诉类似,只是在题意基础上加了一些优化,进行了剪枝。
最后关于何时选回溯算法的一些理解:回溯主要可以解决一些组合、排列、子集等等类似的问题,若问题已知子串或集合的大小,如求子串的长度为2的集合,当然可以用for,若不知,如上面求回文的题,它的集合大小是未知的,集合中串的长度都是不同的,在这种情况下不太可能用for来实现,而回溯是可以遍历到所有的子集或子串。