让字符串成为回文串的最少插入次数(31-37)

92 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 31 天,点击查看活动详情

1312. 让字符串成为回文串的最少插入次数 - 力扣(Leetcode)

题目描述

给你一个字符串 s ,每一次操作你都可以在字符串的任意位置插入任意字符。

请你返回让 s 成为回文串的 最少操作次数 。

「回文串」是正读和反读都相同的字符串。

 

示例 1:

输入: s = "zzazz"
输出: 0
解释: 字符串 "zzazz" 已经是回文串了,所以不需要做任何插入操作。

示例 2:

输入: s = "mbadm"
输出: 2
解释: 字符串可变为 "mbdadbm" 或者 "mdbabdm"

示例 3:

输入: s = "leetcode"
输出: 5
解释: 插入 5 个字符后字符串变为 "leetcodocteel" 。

 

提示:

  • 1 <= s.length <= 500
  • s 中所有字符都是小写字母。

题目分析

本题为线性DP的一道问题。

题目要求输出使当前字符串 ss 成为最长回文字串的操作数,即求最长的回文子串的长度,操作数为字符串长度 n最长回文子串长度n-最长回文子串长度

我们知道,回文子串会有一个中心对称轴,沿轴向左向右字符依次相等。

于是,我们换一种思路,首先定义另一个字符串 sfsf 为字符串 ss 的逆串,即 s[i]=sf[ni1]s[i] = sf[n-i-1],并求取字符串 sssfsf 的最长公共子序列长度,这个值即为字符串 ss 的最长公共子序列长度。

例如,s="acbba"s="acbba",得到 sf="abbca"sf="abbca",最长公共子序列为 abbaabba,这也是字符串 ss 的最长回文子串。

定义 f[i][j]f[i][j] 表示 ss 的前 ii 个字符和 sfsf 的前 jj 个字符的最长公共子序列,最终的操作数便为 nf[n][n]n-f[n][n]

具体的求最长公共子序列的分析见上次题解。

Accept代码

题目来源于力扣,代码规范为力扣

class Solution {
    int f[510][510];
public:
    int minInsertions(string s) {
        string sf = s;
        reverse(s.begin(), s.end());
        s = ' ' + s, sf = ' ' + sf;

        int n = s.size();
        for (int i = 1; i <= n; i ++)
            for (int j = 1; j <= n; j ++)
            {
                f[i][j] = max(f[i - 1][j], f[i][j - 1]);
                if (s[i] == sf[j]) f[i][j] = f[i - 1][j - 1] + 1;
            }        

        return (n - f[n][n]);
    }
};
#define N 510
int a[N], b[N], f[N][N];
int minInsertions(char * s){
    int n = strlen(s);
    for (int i = 1; i <= n; i ++)
    {
        a[i] = s[i - 1];
        b[i] = s[n - i];
    }
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= n; j ++)
        {
            f[i][j] = f[i - 1][j] > f[i][j - 1] ? f[i - 1][j] : f[i][j - 1];
            if (a[i] == b[j]) f[i][j] = f[i - 1][j - 1] + 1;
        }
    return n - f[n][n];
}