LeetCode 1312. 让字符串成为回文串的最少插入次数--区间DP

  1. 让字符串成为回文串的最少插入次数

给你一个字符串 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 中所有字符都是小写字母。

题解:

很有趣的题目,同样的,从动态规划的角度考虑,我们从区间进行拓展,因为是字符串要求变成回文串,我们判断回文串往往是从外面向里面比较,所以我们从两端向内部拓展,于是你会发现,针对字符串s,无非是两种情况
1.s[i]==s[j]
2.s[i]!=s[j]
于是我们定义一个递归函数solve(i,j,s),这个函数可以返回字符串s区间[i,j]构成回文串需要的最小操作数目。
针对情况1比较好处理,因为s[i]本就等于s[j],所以直接跳过,i++,j–,然后再考虑此时的区间[i,j]怎么处理变成回文串,返回答案solve(i+1,j-1)。
针对情况2,动态规划就来了,就需要比较在左边插入一个s[j]好还是在右边插入一个s[i]好,于是我们有了两种结果,返回min(solve(i+1,j)+1,solve(i,j-1)+1)。

是很好理解的吧,然后我们得到了递归函数如下:

    int solve(int l,int r,string s)
    {
        if(l>=r)return 0;
        if(s[l]==s[r])
        return solve(l+1,r-1,s);
        else return min(solve(l+1,r,s)+1,solve(l,r-1,s)+1);
    }

一开始提交上面这个函数,没有问题,但是超时,因为递归函数肯定超时,但是递归函数的好处就是很直观,很能方便我们理解解题思路,所以这里把递归函数贴出来,现在用数组表示动态规划过程,数组就是参考上面的递归函数进行处理。
AC代码

class Solution {
public:
    int dp[505][505];
    int minInsertions(string s) {
        memset(dp,0,sizeof(dp));
        for(int i=0;i<s.length();i++)
        for(int j=i+1;j<s.length();j++)
        dp[i][j]=1e9;
        for(int j=0;j<s.length();j++)
        {
            for(int i=j-1;i>=0;i--)
            {
                if(s[i]==s[j])
                {
                    dp[i][j]=dp[i+1][j-1];//不用考虑
                }
                else
                {
                    dp[i][j]=min(dp[i+1][j]+1,dp[i][j-1]+1);
                }
            }
        }
        return dp[0][s.length()-1];
    }
};

LeetCode 1312. 让字符串成为回文串的最少插入次数--区间DP_第1张图片
PS:做了很多动态规划的题目,发现很有趣的就是,很多题目都是区间DP处理,而区间DP大都是要么从一边拓展到另外一边,要么就是上述这种两端向内部包围的这种,往后的做题如果没思路可以优先从这两个角度思考构建转移关系,其次递归函数很重要,因为有了思路然后再用数组实现具体的动态规划转移是比较麻烦的,但是我们可以直接写一个很直观的递归函数先方便我们理解,确保思路没问题,再按照递归函数转换成数组表示,非常有趣。

你可能感兴趣的:(LeetCode,动态规划,字符串,动态规划,算法)