【编程刷题笔记】让字符串成为回文串的最少插入次数

本文介绍了一种使用动态规划解决字符串问题的方法,具体为计算将任意字符串转化为回文串所需的最少插入次数。通过逐步解析算法思想及实现细节,帮助读者理解如何构造状态转移方程。

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

题目

给你一个字符串 s ,每一次操作你都可以在字符串的任意位置插入任意字符。请你返回让 s 成为回文串的 最少操作次数 。
「回文串」是正读和反读都相同的字符串。

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

示例 2:
输入:s = “mbadm”
输出:2
解释:字符串可变为 “mbdadbm” 或者 “mdbabdm” 。

示例 3:
输入:s = “leetcode”
输出:5
解释:插入 5 个字符后字符串变为 “leetcodocteel” 。

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

leetcode原题链接

解题思路:

这种关于字符串的最长序列、最少修改次数等问题,大概率逃不过动态规划
很朴素的一个思路是,我们创造一个数组dp[i][j],用来存储字符串s[i:j]成为回文串的最少插入次数。根据动态规划的思想,需要建立状态转移方程。我们通过题意可以得出以下结论:
1.如果 s[i] == s[j],那么最外层已经形成了回文,我们只需要继续考虑 s[i+1:j-1];
2.如果 s[i] != s[j],那么我们要么在 s[i:j] 的末尾添加字符 s[i],要么在 s[i:j] 的开头添加字符 s[j],才能使得最外层形成回文。如果我们选择前者,那么需要继续考虑 s[i+1:j];如果我们选择后者,那么需要继续考虑 s[i:j-1]。

这个题目之所以会成为一个难度系数是困难的题目,我个人觉得其难点在于二维dp的遍历和初始化,如果我们按照正常思路进行遍历dp的话,会发现dp[i][j] 的取值与dp[i + 1][j], dp[i][j - 1],dp[i + 1][j - 1]有关,但是如果选择从下往上遍历,则第一步所求就是题目的要求答案,简单来说,就是想用动归求解A的答案,但是动归的初始化就是A的答案。陷入了死循环。因此本题目的难点在于怎么遍历二维dp

一种可行的方法是:
递增地枚举子串 s[i:j] 的长度 span = j - i + 1,再枚举起始位置 i,通过 j = i + span - 1 得到 j 的值并计算 dp[i][j]。这样的计算顺序可以保证在计算 dp[i][j] 时,状态转移方程中的状态 dp[i + 1][j],dp[i][j - 1] 和 dp[i + 1][j - 1] 均已计算过

class Solution:
    def minInsertions(self, s: str) -> int:
        n = len(s)
        dp = [[0] * n for _ in range(n)]
        for span in range(2, n + 1):
            for i in range(n - span + 1):
                j = i + span - 1
                dp[i][j] = min(dp[i + 1][j], dp[i][j - 1]) + 1
                if s[i] == s[j]:
                    dp[i][j] = min(dp[i][j], dp[i + 1][j - 1])
        return dp[0][n - 1]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值