【动态规划】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];

    }
}

  

posted @ 2021-01-28 10:54  A_Aron  阅读(102)  评论(0)    收藏  举报
//一下两个链接最好自己保存下来,再上传到自己的博客园的“文件”选项中