分割回文串
题目:
给出一个字符串s,分割s使得分割出的每一个子串都是回文串
计算将字符串s分割成回文分割结果的最小切割数
例如:给定字符串s="aab"
,
返回1,因为回文分割结果["aa","b"]
是切割一次生成的。
解题思路:
还是抓住动态规划的解题步骤
初始值
递推的过程
转移方程
打印 dp
数组
返回值
最开始还是要先进行特殊值处理:
当字符串的长度为0,1 的时候,是不需要进行分割的!例如:
s = "";
s = "A";
即:
if(s.length() == 0 || s.length() == 1) return 0;
递推的过程很是玄幻的感觉。而分割字符串我们首先应该做的是假设他全部都不是回文串,例如:ABCDEFG
。
所以我们需要给dp
一个初始值:
int[] dp = new int[s.length()+1];
for(int i = 0;i < s.length()+1;i++) {
dp[i] = i-1;
}
现在来递推,当然我们需要给一个易于理解的例子:ABCCBD;
同时,也需要双指针 i ,j
i 代表当前字符串
j 代表左边界
dp[i] 中存放的内容是分割的次数
i = 3 的时候表示的是当前字符串的内容是
"ABC"
j 的含义是从 j 到 i 是否存在回文串,j = 1 表示的是当前字符串是"BC"
只有A的时候——》 F(1) = 0;
只有AB的时候——》 F(2) = F(1) + 1 = 1;
只有ABC的时候——》 F(3) = F(2) + 1 = 2;
只有ABCC的时候——》 F(4)Math.min (F(3) + 1,F(2) + 1) = 2;
我们可以选择在 A B C 的基础上再分割一次,或者在 A B 的基础上分割一次
只有ABCCB的时候——》 F(5) = Math.min(F(1) + 1,F(4) + 1) = 2;
在 A 的基础上分割一次,或者在 ABCC 的基础上分割一次
全部ABCCBD的时候——》F(6) = F(5) + 1 = 2;
递推完成:我们发现,当只有存在回文串的时候,才会进入我们才会面临选择:是在前一个字符的基础上分割一次,还是在回文串的开始到结束的基础上分割一次。
转移方程:
if(当前字符串是否是回文串) {
F(i) = Math.min(dp[i],dp[j]+1};
}
返回值
dp[s.length];
我们来看一下整体代码:
public static int minCut (String s) {
//特殊处理
if(s.length() == 0 || s.length() == 1) return 0;
int[] dp = new int[s.length()+1];
//初始化
/*至于 `dp[0] = -1
如果这个字符串本身就是回文串的话,
当 i = s.length, j == 0 的时候,
进入转移方程 dp[i] = min(dp[0] + 1,dp[i]) = 1
*/
for(int i = 0;i < s.length()+1;i++) {
dp[i] = i-1;
}
for(int i = 1;i < s.length()+1;i++) {
for(int j = 0;j < i;j++) {
//判断是否是回文
if(isX(s,i-1,j)) {
dp[i] = Math.min(dp[j]+1,dp[i]);
}
}
//打印dp数组内容
System.out.println(Arrays.toString(dp));
}
return dp[s.length()];
}
//判断回文串
public static boolean isX(String str,int i,int j) {
while(j < i) {
if(str.charAt(j) != str.charAt(i)) {
return false;
}
i--;
j++;
}
return true;
}
来看下dp
数组
大概过程就是这个哑子,这个解法由于有判断是否是回文序列,时间复杂度接近O(n^2) * O(n) = O(n^3)。
优化:
将回文序列的判断变为一个二维数组
public static boolean[][] isPal(String s) {
int len = s.length();
boolean[][] mat = new boolean[len][len];
for (int i = len-1; i >= 0; i--) {
for (int j = i; j < len; j++) {
if (i == j) {//i == j 的时候 s.charAt(i) == s.charAt(j)
mat[i][j] = true;
}else if (i+1 == j && s.charAt(i) == s.charAt(j)) {//字符相邻且相等
mat[i][j] = true;
}else {//不相邻但是相等且s.charAt(i)——s.charAt(j)包裹的里面一层是回文序列
mat[i][j] = s.charAt(i) == s.charAt(j) && mat[i+1][j-1];
}
}
}
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
System.out.print(mat[i][j] + " ");
}
System.out.println();
}
return mat;
}
优化后的代码:
public static int minCut1(String s) {
if (s.length() == 0 || s.length() == 1) return 0;
boolean[][] mat = isPal(s);
int[] dp = new int[s.length()+1];
for(int i = 0;i < s.length()+1;i++) {
dp[i] = i-1;
}
for(int i = 1;i < s.length()+1;i++) {
for(int j = 0;j < i;j++) {
if(mat[j][i-1]) {
dp[i] = Math.min(dp[j]+1,dp[i]);
}
}
System.out.println(Arrays.toString(dp));
}
return dp[s.length()];
}
冲啊!!!
如有错误,多多指教!