一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情。
一.题目:
1312. 让字符串成为回文串的最少插入次数 给你一个字符串
s,每一次操作你都可以在字符串的任意位置插入任意字符。
请你返回让 s 成为回文串的 最少操作次数 。
「回文串」是正读和反读都相同的字符串。
示例 1:
输入: s = "zzazz"
输出: 0
解释: 字符串 "zzazz" 已经是回文串了,所以不需要做任何插入操作。
示例 2:
输入: s = "mbadm"
输出: 2
解释: 字符串可变为 "mbdadbm" 或者 "mdbabdm" 。
示例 3:
输入: s = "leetcode"
输出: 5
解释: 插入 5 个字符后字符串变为 "leetcodocteel" 。
提示:
1 <= s.length <= 500s中所有字符都是小写字母。
二、思路分析:
今天继续来巩固动态规划类型的题目,这道题目难度显示为困难,但是如果你熟悉动态规划,就会发现这道题的思路特别简单。题目的思路如下:
- 定义dp数组:我们定义
dp[i][j]为从i到j位置成为回文串的最小操作数,所以最终求得结果就是从0到n-1的最小操作数dp[0][n-1]。 - 进行初始化:我们知道每一个字符就是一个回文串,所以每个字符的最小操作数是
0,因为它不需要进行操作。 - 明确状态转移方程:我们根据题目得知,如果发现
i与j不相同后,我们只能进行插入操作,即可能是i+1的位置进行插入操作,也可能是j-1的位置,所以情况全部写出来然后取最小值即可:dp[i][j] = Math.min(dp[i + 1][j], dp[i][j - 1]) + 1;如果相同则不需要进行插入操作。 - 根据状态转移方程明确遍历顺序:由状态转移方程得知遍历顺序是从下到上,从左到右的,所以可以确定遍历位置。
三、代码:
/**
* @param {string} s
* @return {number}
*/
var minInsertions = function (s) {
let n = s.length
//dp定义:dp[i][j]为从i到j位置成为回文串的最小操作数
let dp = Array.from(new Array(n), () => new Array(n).fill(0) )
//初始化:每个字符都是回文串,所以dp[i][j] == 0
for (let i = 0; i < n; i++) {
dp[i][i] = 0
}
for (let i = n - 2; i >= 0; i--) {
for (let j = i+1; j < n; j++) {
//状态转移方程:s[i]与s[j]相等,dp[i][j] = dp[i]
if (s[i] == s[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][n-1]
};
四、总结:
这样的最值问题一般都是采用
动态规划进行求解问题的,所以一定要掌握动态规划的核心思想:即数组的定义、状态转移方程等,如果不能够确定遍历顺序,需要通过画图进行理解。