学习目标:
目标:熟练运用Java所学知识
学习内容:
本文内容:使用java解决 分割回文串
题目描述
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文。
返回符合要求的 最少分割次数 。
示例 1:
输入:s = “aab”
输出:1
解释:只需一次分割就可将 s 分割成 [“aa”,“b”] 这样两个回文子串。
示例 2:
输入: s = “a”
输出: 0
示例 3:
输入:s = “ab”
输出: 1
解题思路
方法:动态规划
状态:
子状态:到第1,2,3,…,n个字符需要的最小分割数
F(i): 到第i个字符需要的最小分割数
状态递推:
F(i) = min{F(i), 1 + F(j)}, where j<i && j+1到i是回文串
上式表示如果从j+1到i判断为回文字符串,且已经知道从第1个字符 到第j个字符的最小切割数,那么只需要再切一次,就可以保证 1–>j,j+1–>i都为回文串。
初始化:
F(i) = i - 1
上式表示到第i个字符需要的最大分割数
比如单个字符只需要切0次,因为单子符都为回文串
2个字符最大需要1次,3个2次…
返回结果:
F(n)
代码中有两个方法,方法一时间复杂度比方法二时间复杂度高,方法二优化了判断回文的方法
实现代码
- 方法一:
public class MinCut {
public int minCut(String s) {
// write code here
int length = s.length();
int[] res = new int[length + 1];
for (int i = 0; i <= length; i++) {
//初始化
res[i] = i - 1;
}
for (int i = 1; i <= length; i++) {
for (int j = i - 1; j >= 0; j--) {
//判断从j到i位置的字符串是不是回文串
if (isP(s.substring(j, i))) {
//F(i) = min{F(j)+1,F(i)},
res[i] = Math.min(res[j] + 1, res[i]);
}
}
}
return res[length];
}
//判断回文
public static boolean isP(String s) {
char[] chars = s.toCharArray();
int left = 0;
int right = chars.length - 1;
while (left < right) {
//当left位置和right位置元素不相等,则不是回文串,返回false
if (chars[left] != chars[right]) {
return false;
}
left++;
right--;
}
//while循环结束表示没有找到不相等的位置,则是回文串
return true;
}
}
- 方法二
以上代码复杂度过高,主方法里面的两次循环复杂度是O(n^2),判断回文的时间复杂度是O(n),所以整体是O(n ^3),我们可以继续优化判断回文串的方法,将判断回文串的方法返回一个二位数组,数组中(i,j)位置元素表示第i个到第j个元素是不是回文串
判断回文方法的动态规划思路:
我们使用 D(i,j) 表示字符串从i 到 j 的位置是不是回文串 true表示是回文串,false表示不是回文串 所以不难得出:
D(i,j)是否是回文串依赖于 D(i+1,j-1),
也很容易得到初始状态D(i.i)=true
代码:
public class MinCut2 {
public int minCut(String s) {
int length=s.length();
boolean[][] matrix=isP(s);
int[] res=new int[length+1];
for(int i=0;i<=length;i++){
res[i]=i-1;
}
for(int i=1;i<=length;i++){
for(int j=i-1;j>=0;j--){
if(matrix[j][i-1]){
res[i]=Math.min(res[i],res[j]+1);
}
}
}
return res[length];
}
//返回判断回文串的二位数组
public static boolean[][] isP(String s){
char[] c=s.toCharArray();
int length=s.length();
boolean[][] res=new boolean[length][length];
for(int i=0;i<length;i++){
//初始化,只有一个元素时,直接是回文串
res[i][i]=true;
}
for(int i=1;i<length;i++){
for(int j=i-1;j>=0;j--){
if(c[i]!=c[j]){
//两个位置元素不相等
res[j][i]=false;
}else{
//两个位置元素不相等
if(res[j+1][i-1]||i-j<3){
//1.执行到这个else if 表示chars[i]=chars[j]
//2.条件i-j<3表示在1的条件下,当前子串只有3个元素,为回文串
//3.当res[j+1][i-1]为true时,当前子串为回文串,
// 因为D(i,j)是否是回文串依赖于 D(i+1,j-1),
res[j][i]=true;
}else{
res[j][i]=false;
}
}
}
}
return res;
}
}