//这是对前面回文串分割的优化算法,这个时间复杂度是O(n^2) // 方法是计算最小分割次数时,不用再去遍历字符串判断是否回文,而是先将字符串是否回文的结果保存下来,用一个二维数组来保存,i表示开始字符,j表示结束字符 //判断字符串是否回文就变成了求(i,j)区间是否是回文字符串,因此也可以用动态规划算法来求解,dp[i,j]表示从i到j字符串是回文的 //转移方程:判断(i,j)是否回文>>>s[i]==s[j] && (i+1.j-1)回文 //由于判断从i开始的字符串是否回文需要知道从i+1开始是否回文,所以我们需要从后往前开始递推,也就是从字符串末尾开始遍历 //初始化:当i==j也就是只有一个字符时,dp[i,j]结果是true;当i+1==j时,如果是s[i]==s[j]那么dp[i,j]是true;其他情况当s[i]==s[j] && dp[i+1.j-1]时,dp[i,j]是true;
public class OptimizePalindromeString {
public static boolean[][] ispalind(String s){
int len=s.length();
//二维数组存储(i,j)区间的是否回文的真值只需要更新矩阵的一半,利用二维数组来保存i到j字符串的回文真值情况
boolean[][] palind=new boolean[len][len];
for(int i=len-1;i>=0;i--){//i表示行
for(int j=i;j<len;j++ ){//j表示列
if(j==i){
//i和j是同一个字符下标
palind[i][j]=true;
}else if(j==i+1){
//i和j间隔一个字符
palind[i][j]=s.charAt(i)==s.charAt(j);
}else{
//i和j间隔多个字符
palind[i][j]=s.charAt(i)==s.charAt(j) && palind[i+1][j-1];
}
}
}
return palind;
}
public static int minCut (String s) {
int len=s.length();
if(len==0){
return 0;
}
int[] mincut=new int[len+1];
boolean[][] palind=ispalind(s);
//首先初始化前i个字符的切割次数是字符个数减1
for(int i=1;i<=len;i++){//从第一个字符串开始记录初始切割次数
mincut[i]=i-1;
}
for(int i=2;i<=len;i++){//i是遍历字符串的,因为第一个字符的最小切割次数就是0,所以直接从第二个字符开始判断
if(palind[0][i-1]){
//第i个字符的索引是i-1,如果前i个字符是回文那么直接得出前i个字符的最小切割次数就是0;
mincut[i]=0;
continue;
}
for(int j=1;j<i;j++){//j是用来找最小切割次数的,找前i个字符的最小切割次数时,如果前i个不回文,那么就要从第一个字符开始去找最小切割次数知道i下标
if(palind[j][i-1]){
//如果从j+1到i个字符回文,那么就mincut[i]就是利用算出的前j个字符的最小切割次数再+1即可
mincut[i]=Math.min(mincut[i],mincut[j]+1);
}
}
}
return mincut[len];
}
}