【动态规划】dp状态的压缩
1312. 让字符串成为回文串的最少插入次数
给你一个字符串 s ,每一次操作你都可以在字符串的任意位置插入任意字符。
请你返回让 s 成为回文串的 最少操作次数 。
「回文串」是正读和反读都相同的字符串。
示例 1:
输入:s = "zzazz"
输出:0
解释:字符串 "zzazz" 已经是回文串了,所以不需要做任何插入操作。
示例 2:
输入:s = "mbadm"
输出:2
解释:字符串可变为 "mbdadbm" 或者 "mdbabdm" 。
示例 3:
输入:s = "leetcode"
输出:5
解释:插入 5 个字符后字符串变为 "leetcodocteel" 。
示例 4:
输入:s = "g"
输出:0
示例 5:
输入:s = "no"
输出:1
提示:
1 <= s.length <= 500
s 中所有字符都是小写字母。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-insertion-steps-to-make-a-string-palindrome
首先是常规的二维dp解法,比较容易理解。
class Solution {
public int minInsertions(String s) {
char[] c=s.toCharArray();
int length=c.length;
int dp[][]=new int[length][length];//c[i][j]变成回文串需要的操作次数
//base case:dp[i][i]=0;
for(int i=length-1;i>=0;i--){
for(int j=i+1;j<length;j++){
if(c[i]==c[j]){
dp[i][j]=dp[i+1][j-1];
}else{
dp[i][j]=Math.min(dp[i+1][j],dp[i][j-1])+1;
}
}
}
return dp[0][length-1];
}
}
二维dp数组还可以进一步优化,把二维数组投影到一维数组。
因为 for 循环遍历 i 和 j 的顺序为从左向右,从下向上,所以可以发现,在更新一维 dp 数组的时候,dp[i+1][j-1] 会被 dp[i][j-1] 覆盖掉,图中标出了这四个位置被遍历到的次序:

那么如果我们想得到 dp[i+1][j-1],就必须在它被覆盖之前用一个临时变量 temp 把它存起来,并把这个变量的值保留到计算 dp[i][j] 的时候。
class Solution {
public int minInsertions(String s) {
char[] c=s.toCharArray();
int length=c.length;
int dp[]=new int[length];//c[i][j]变成回文串需要的操作次数
//base case:dp[i][i]=0;
for(int i=length-1;i>=0;i--){
// 存储 dp[i+1][j-1] 的变量,也就是上一轮i的dp[j]
int pre=0;
for(int j=i+1;j<length;j++){
int temp=dp[j];//此时dp[j]还没更新过,是上一轮的结果dp[i+1][j],用temp保存
if(c[i]==c[j]){
// dp[i][j] = dp[i+1][j-1]
dp[j]=pre;
}else{
//min(dp[i+1][j],dp[i][j-1])
dp[j]=Math.min(dp[j],dp[j-1])+1;
//dp[j]是上一轮i算出来的,对应dp[i+1][j];
//dp[j-1]是这一轮i的上一个j算出来的,对应dp[i][j-1];
}
// 到下一轮循环,pre 就是 dp[i+1][j-1] 了
pre=temp;
}
}
return dp[length-1];
}
}

浙公网安备 33010602011771号