题目
点击这里:传送
思路
(一)定义数组元素的含义
dp[i][j] 表示s[i..j]中最长回文子序列的长度
所以只有当i<=j时,dp数组中才有值。
(二)找出关系数组元素间的关系式
经过分析,可以分成三大类讨论
1.str[i,j]只有一个字符,即i==j。即其本身可以形成回文,即dp[i][j]=0
2.str[i,j] 有两个字符
2.1 当str[i]==str[j],即两个字符相同时,dp[i][j]=0。
2.2 当str[i]!=str[j],即两个字符不同时,例如,AB,可以在前面加一个B,变成BAB,也可以在后面加一个A,变成ABA,即dp[i][j]=1。
3.str[i...j]有三个或者三个以上字符,属于一般的情况。
3.1 当str[i]==str[j]时,例如,ABDCA,只需要考虑BDC,也即要求dp[i][j],转化为求dp[i+1][j-1],即转移方程:dp[i][j]=dp[i+1][j-1]。
3.2 当str[i]!=str[j]时,有两种方法使其变成回文。
一种是先让str[i,j-1]变成回文,在i的左边添加str[j],整体就会变成回文。
例如ABECD,可以先考虑ABEC这部分。
一个方案,ABECEBA,再在ABECEBA左边加上str[j],即D,变成了DABECEBAD;
另外一种方案,同理,先让str[i+1,j]变成回文,BECDCEB,再在右边加上A,最终变成了整体回文ABECDCEBA。
动态转移方程式:
要想使str[i,j-1]变成回文,其最少的添加次数是dp[i][j-1],再加上往左侧补的字符(往右加同理),最终应该是dp[i][j-1]+1。str[i+1,j]同理。
所以,dp[i][j]=min(dp[i][j-1],dp[i+1][j])+1
(三)找出初始值
-
当
i == j
时dp[i][j] = 0
,这时候s[i..j]
就是单个字符,本身就是回文串,不需要任何插入。 -
len(str[i,j]) == 2
时,-
当
str[i]==str[j]
,即两个字符相同时,dp[i][j]=0
。 -
当
str[i]!=str[j]
,即两个字符不同时,例如,AB,可以在前面加一个B,变成BAB,也可以在后面加一个A,变成ABA,即dp[i][j]=1
。
-
-
最终的答案是
dp[0][n-1]
(n
是字符串s
的长度)。
这里借用一下labuladong大佬的一张讲解图,我稍微补充了一下s[i] == s[j]的情况。
代码
public class Solution {
public int minInsertions(String s) {
int l = s.length();
if (l <= 1) {
return 0;
}
int[][] dp = new int[l][l];
// for(int i=0; i<=l-1;i++){
// dp[i][i] = 0;
// }
// 从下往上
for (int i = l - 2; i >= 0; i--) {
// 从左到右
for (int j = i + 1; j <= l - 1; j++) {
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = dp[i + 1][j - 1];
} else {
dp[i][j] = Math.min(dp[i + 1][j], dp[i][j - 1]) + 1;
}
}
}
// 根据 dp 数组的定义,题目要求的答案
return dp[0][l - 1];
}
}
参考资料
labuladong大佬的算法讲解很到位,关注一下,offer手到擒来。