【Leetcode】1312. Minimum Insertion Steps to Make a String Palindrome

题目地址:

https://leetcode.com/problems/minimum-insertion-steps-to-make-a-string-palindrome/

给定一个长 n n n的字符串 s s s,允许在任意位置插入任意字符,问至少插入多少次可以使得 s s s成为回文串。

思路是动态规划。设 f [ l ] [ r ] f[l][r] f[l][r] s [ l : r ] s[l:r] s[l:r]需要变成回文串至少需要多少次插入,则我们需要返回 f [ 0 ] [ n − 1 ] f[0][n-1] f[0][n1]。那么当 l = r l=r l=r f [ l ] [ r ] = 0 f[l][r]=0 f[l][r]=0,一个字母自己就是回文串;如果 l = r − 1 l=r-1 l=r1,那么就看 s [ l ] = s [ r ] s[l]=s[r] s[l]=s[r]是否成立,如果是,则不需要插入, f [ l ] [ r ] = 0 f[l][r]=0 f[l][r]=0,否则显然 f [ l ] [ r ] = 1 f[l][r]=1 f[l][r]=1。对于其余情况,我们考虑如何递推。如果 s [ l ] = s [ r ] s[l]=s[r] s[l]=s[r],那么只需要让 s [ l + 1 : r − 1 ] s[l+1:r-1] s[l+1:r1]变成回文串就行了,那么此时 f [ l ] [ r ] = f [ l + 1 ] [ r − 1 ] f[l][r]=f[l+1][r-1] f[l][r]=f[l+1][r1](严格证明亦不难,如果某种最优方案能使得 s s s变成回文串,那么最终的回文串必然以 s [ l ] s[l] s[l]开头和结尾,否则的话就说明在两边添加了多余的字符,这是不必要的;那么既然以 s [ l ] s[l] s[l]开头和结尾,任意的插入操作都可以视为是对 s [ l + 1 : r − 1 ] s[l+1:r-1] s[l+1:r1]的插入操作,那么就可以用归纳假设了,至少需要 f [ l + 1 ] [ r − 1 ] f[l+1][r-1] f[l+1][r1]步);如果 s [ l ] ≠ s [ r ] s[l]\ne s[r] s[l]=s[r],那么最优方案得到的最终的回文串不能同时以 s [ l ] s[l] s[l]做开头与以 s [ r ] s[r] s[r]做结尾,但是必须或者以 s [ l ] s[l] s[l]做开头,或者以 s [ r ] s[r] s[r]做结尾,否则的话说明在两边添加多余字符了,矛盾,既然如此,如果以 s [ l ] s[l] s[l]开头,那么必然是先将 s [ l + 1 : r ] s[l+1:r] s[l+1:r]变成了回文串,然后在后面加一个 s [ r ] s[r] s[r](当然顺序可能不是如此,但无论怎样一定会在最后添加一个 s [ l ] s[l] s[l],我们调整插入顺序使得这一步最后做,那么前面的步骤其实就是在使得 s [ l + 1 : r ] s[l+1:r] s[l+1:r]变成回文串),所以此时 f [ l ] [ r ] = min ⁡ { f [ l + 1 ] [ r ] , f [ l ] [ r − 1 ] } + 1 f[l][r]=\min\{f[l+1][r],f[l][r-1]\}+1 f[l][r]=min{f[l+1][r],f[l][r1]}+1。综上: f [ l ] [ r ] = { f [ l + 1 ] [ r − 1 ] , s [ l ] = s [ r ] min ⁡ { f [ l + 1 ] [ r ] , f [ l ] [ r − 1 ] } + 1 , s [ l ] ≠ s [ r ] f[l][r]=\begin{cases}f[l+1][r-1],s[l]=s[r]\\\min\{f[l+1][r],f[l][r-1]\}+1,s[l]\ne s[r] \end{cases} f[l][r]={f[l+1][r1],s[l]=s[r]min{f[l+1][r],f[l][r1]}+1,s[l]=s[r]代码如下:

public class Solution {
    public int minInsertions(String s) {
        int n = s.length();
        int[][] f = new int[n][n];
        for (int len = 2; len <= n; len++) {
            for (int l = 0; l + len - 1 < n; l++) {
                int r = l + len - 1;
                char ch1 = s.charAt(l), ch2 = s.charAt(r);
                if (len == 2) {
                    f[l][r] = ch1 == ch2 ? 0 : 1;
                } else {
                    f[l][r] = ch1 == ch2 ? f[l + 1][r - 1] : Math.min(f[l + 1][r], f[l][r - 1]) + 1;
                }
            }
        }
        
        return f[0][n - 1];
    }
}

时空复杂度 O ( n 2 ) O(n^2) O(n2)

C++:

class Solution {
 public:
  int minInsertions(string s) {
    int n = s.size();
    int f[n][n];
    memset(f, 0, sizeof f);
    for (int len = 2; len <= n; len++)
      for (int l = 0; l + len - 1 < n; l++) {
        int r = l + len - 1;
        if (s[l] == s[r]) f[l][r] = f[l + 1][r - 1];
        else f[l][r] = min(f[l + 1][r], f[l][r - 1]) + 1;  
      }

    return f[0][n - 1];
  }
};

时空复杂度一样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值