Leetcode [1312] 让字符串成为回文串的最少插入次数 动态规划

复制代码
/*
 * @lc app=leetcode.cn id=1312 lang=cpp
 *
 * [1312] 让字符串成为回文串的最少插入次数
 *
 * https://leetcode-cn.com/problems/minimum-insertion-steps-to-make-a-string-palindrome/description/
 *
 * algorithms
 * Hard (64.47%)
 * Likes:    93
 * Dislikes: 0
 * Total Accepted:    7.5K
 * Total Submissions: 11.6K
 * Testcase Example:  '"zzazz"'
 *
 * 给你一个字符串 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 中所有字符都是小写字母。
 * 
 * 
 */
复制代码

 

 思路:labuladong回文问题终极篇:最小代价构造回文串

我们定义一个二维的dp数组,dp[i][j]的定义如下:对字符串s[i..j],最少需要进行dp[i][j]次插入才能变成回文串

我们想求整个s的最少插入次数,根据这个定义,也就是想求dp[0][n-1]的大小(ns的长度)。

同时,base case 也很容易想到,当i == jdp[i][j] = 0,因为当i == js[i..j]就是一个字符,本身就是回文串,所以不需要进行任何插入操作。

如果我们现在想计算dp[i][j]的值,而且假设我们已经计算出了子问题dp[i+1][j-1]的值了,那么也就可以认为s[i+1..j-1]已经是一个回文串了,所以通过dp[i+1][j-1]推导dp[i][j]的关键就在于s[i]s[j]这两个字符

如果s[i] == s[j]的话,我们不需要进行任何插入,只要知道如何把s[i+1..j-1]变成回文串即可

翻译成代码就是这样:

if (s[i] == s[j]) {
    dp[i][j] = dp[i + 1][j - 1];
}

s[i] != s[j]时,无脑插入两次肯定是可以让s[i..j]变成回文串,但是不一定是插入次数最少的,最优的插入方案应该被拆解成如下流程:

步骤一,做选择,先将s[i..j-1]或者s[i+1..j]变成回文串。怎么做选择呢?谁变成回文串的插入次数少,就选谁呗。

步骤二,根据步骤一的选择,将s[i..j]变成回文

如果你在步骤一中选择把s[i+1..j]变成回文串,那么在s[i+1..j]右边插入一个字符s[i]一定可以将s[i..j]变成回文;同理,如果在步骤一中选择把s[i..j-1]变成回文串,在s[i..j-1]左边插入一个字符s[j]一定可以将s[i..j]变成回文。

那么根据刚才对dp数组的定义以及以上的分析,s[i] != s[j]时的代码逻辑如下:

if (s[i] != s[j]) {
    // 步骤一选择代价较小的
    // 步骤二必然要进行一次插入
    dp[i][j] = min(dp[i + 1][j], dp[i][j - 1]) + 1;
}

综合起来,状态转移方程如下:

if (s[i] == s[j]) {
    dp[i][j] = dp[i + 1][j - 1];
} else {
    dp[i][j] = min(dp[i + 1][j], dp[i][j - 1]) + 1;
}

 

 

复制代码
class Solution {
public:
    int minInsertions(string s) {
        int n=s.size();
        vector<vector<int>> dp(n,vector<int>(n,0));
        for(int i=n-2;i>=0;i--)
        {
            for(int j=i+1;j<n;++j)
            {
                if(s[i]==s[j])
                    dp[i][j]=dp[i+1][j-1];
                else
                    dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1;
            }
        }
        return dp[0][n-1];
    }
};
复制代码

 

posted @   鸭子船长  阅读(471)  评论(0编辑  收藏  举报
编辑推荐:
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
历史上的今天:
2020-05-19 Http 连接复用
2017-05-19 理解Android线程创建流程(转)
2017-05-19 Android进程绝杀技--forceStop(转)
2017-05-19 说说Android应用的persistent属性(转)
2017-05-19 Android四大组件与进程启动的关系(转)
2017-05-19 Android:关于声明文件中android:process属性说明
点击右上角即可分享
微信分享提示