分隔回文串-回溯与动态规划
题目描述
[131. 分割回文串]https://leetcode-cn.com/problems/palindrome-partitioning/
题解
求解所有可能的题目一般都是暴力搜索,没有什么特别的优化方法。
暴力搜索重要的思想是回溯思想:
- 从当前状态开始,依次搜索当前状态下的所有可能选择;
- 对于每个选择,如果是结束状态,那么返回;
- 否则,这个选择又可作为当前状态,回到第一步。
回溯可以用递归的方法实现。
对于本题,由于要穷举所有回文子串,我们可以首先缓存所有连续子串是否是回文串,而这个过程中可以使用动态规划: - 首先,每个单个字符都是回文串。
- 其次,空串也一定是回文串。
- 最后,如果S[i+1 ~ j-1]是回文串,并且s[i]=s[j],那么s[i~j]也一定是回文串。因此s[i ~ j]是否是回文串可以由前面已经求得的更小的子串S[i+1 ~ j-1]是否是回文串,以及s[i]是否等于s[j]来
判断。
长度为n的字符串的连续子串一共有n×n种,因此,我们可以建立一个n×n的bool型数组isReverse[][],isReverse[i][j]如果为true,则代表s[i ~ j]是回文串。
PS:s[i][j],当i=j或i>j是必要的。i=j时,说明单个字符一定回文,而i>j时用于判定s[i ~ i+1]是否回文,由于空串一定回文,只需判断s[i]是否等于s[i+1]。
由于动态规划要由小的串生成大的串,我们可以从字符串末尾开始,过程演示如下:
建立了以上判断数组后,就可以搜索了。
部分搜索过程如下:
代码:
class Solution {
List<List<String>> ans = new ArrayList();
boolean[][] isReverse;
List<String> cur = new ArrayList();
public List<List<String>> partition(String s) {
int n = s.length();
isReverse = new boolean[n][n];
for(int i=0;i<n;i++){
Arrays.fill(isReverse[i],true);
}
for(int i=n-1;i>=0;i--){
for(int j=i+1;j<n;j++){
isReverse[i][j]=(s.charAt(i)==s.charAt(j))&&isReverse[i+1][j-1];
}
}
dfs(s,0,n);
return ans;
}
private void dfs(String s,int begin,int len){
if(begin==len){
ans.add(new ArrayList<String>(cur));//一定要new一个,否则后续改变cur,ans里面也会改变
return ;
}
for(int i=begin;i<len;i++){
if(isReverse[begin][i]){
cur.add(s.substring(begin,i+1));
dfs(s,i+1,len);
cur.remove(cur.size()-1);
}
}
}
}