回文字符串指的是字符串从左到右 和从右到左是一样的。
对于从i到j的子串,判断其回文
s[i]==s[j] &&(flag[i+1][j-1] || j-i<=1)
1、最小回文分割次数
求最小的切分,使得分割后每个子串都是回文的。
状态 dp[i] 表示从i到n-1的最小回文分割数目,
那么对于任意的一个j,j属于[i,n-1],如果[i,j]是回文的话,则
dp[i]=min(dp[j+1]+1) j从i到n-1,且当j=n-1时,dp[n]=-1;
因此只要遍历一遍从i到n-1的j,求取最小的dp[i]即可
//和最长递增子序列很相似
public int minCut(String s) {
int len=s.length();
boolean[][] flag=new boolean[len][len];
//dp[i]代表[i,len-1]的切成回文的最小分割数
int[] dp=new int[len+1];
// getdp(s,flag,len);
//从右往左进行计算
dp[len]=-1;
for(int i=len-1;i>=0;i--){
dp[i]=Integer.MAX_VALUE;
for(int j=i;j<len;j++){
//[i,j]是回文
if(s.charAt(j)==s.charAt(i) && (j-i<=1 || flag[i+1][j-1])){
flag[i][j]=true;
dp[i]=Math.min(dp[j+1]+1,dp[i]);
}
}
}
return dp[0];
}
如果要求出所有的分割情况,而不要求最小的,则应该使用递归回溯
public static List<List<String>> partition(String s) {
List<List<String>> res=new ArrayList<>();
List<String> cur=new ArrayList<>();
dfs(res,cur,s);
return res;
}
/**
*
* @param res 存储每一趟遍历的符合要求的记过
* @param cur cur记录一趟遍历,存储的结果
* @param s
*/
public static void dfs(List<List<String>>res,List<String> cur,String s){
if(s.length()==0){
res.add(new ArrayList<>(cur));
}
int len=s.length();
for(int i=1;i<=len;i++){
String s1=s.substring(0,i);
if(isPalindrome(s1)){
cur.add(s1);
String s2=s.substring(i);
dfs(res,cur,s2);
cur.remove(cur.size()-1);
}
}
}
public static boolean isPalindrome(String s){
if(s==null||s.length()==0){
return false;
}
int j=s.length()-1;
int i=0;
while(i<j){
if(s.charAt(i)==s.charAt(j)){
i++;
j--;
}else{
return false;
}
}
return true;
}
2、最长回文子串
可以借用1中的方式,求出一个dp数组,保存[i,j]是否为回文的信息,对于一个从i到j的子串,如何判断它是否为回文串
由此可见dp[i][j]依赖于dp[i+1][j-1],我们要计算出dp[i][j]必须先求出dp[i+1][j-1],而这个位置是在(i,j)的下方,因此我们填充dp这个表格数组的时候遵从的是从做到有从下到上的生成方式。
//为什么不能从i=0到i=n-1 而是要从i=n-1到i=0因
//flag[i][j]依赖于 flag[i+1][j-1]因此 要先计算出i+1行 ,j-1列才能推算出i,j
//因此方向为从下到上,从左到右
public String longestPalindrome(String s) {
if(s==null || s.length()==0){
return "";
}
int n=s.length();
boolean[][] flag=new boolean[n][n];
int[] dp=new int[n];
for(int i=0;i<n;i++){
dp[i]=1;
}
char[]t =s.toCharArray();
//记录长度
int res=0;
//记录起始位置
int index=-1;
for(int i=n-1;i>=0;i--){
for(int j=i;j<n;j++){
//[i,j]是回文,dp[i]表示[i,len-1]上的最长回文串,dp[i]有一个初始值
//现在找到了一个 i~j 长度为j-i+1, j一直变化,能够找到以i开头的最大长度
if(t[i]==t[j]&& (j-i<=1||flag[i+1][j-1])){
flag[i][j]=true;
//从i到j是回文,那么[i,len-1]的最长回文 至少有j-i+1;
dp[i]=Math.max(dp[i],j-i+1);
}
}
if(res<dp[i]){
res=dp[i];
index=i;
}
}
return s.substring(index,index+res);
}