leetcode
leetcode 1201 ~ 1250
让字符串成为回文串的最少插入次数

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

难度:

标签:

题目描述

代码结果

运行时间: 138 ms, 内存: 16.0 MB


/*
 * Problem: Given a string s, you can insert characters at any position to make it a palindrome.
 * You need to find the minimum number of insertion operations required.
 * A palindrome is a string that reads the same backward as forward.
 *
 * Approach: 
 * 1. We need to find the longest palindromic subsequence (LPS) in the string.
 * 2. The minimum number of insertions required will be the length of the string minus the length of the LPS.
 * 3. Use dynamic programming to find the LPS. 
 * 4. Java Streams are not well-suited for this problem's dynamic programming approach,
 *    hence using standard Java.
 */

public class SolutionStream {
    public int minInsertions(String s) {
        int n = s.length();
        int[][] dp = new int[n][n];
        for (int i = 0; i < n; i++) dp[i][i] = 1;
        for (int len = 2; len <= n; len++) {
            for (int i = 0; i <= n - len; i++) {
                int j = i + len - 1;
                if (s.charAt(i) == s.charAt(j)) {
                    dp[i][j] = dp[i + 1][j - 1] + 2;
                } else {
                    dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
                }
            }
        }
        int lps = dp[0][n - 1];
        return n - lps;
    }
}

解释

方法:

本题解通过动态规划方法求解字符串成为回文串的最少插入次数。思路是先求出给定字符串的最长回文子序列的长度(使用动态规划表dp),然后用字符串的总长度减去这个最长回文子序列的长度,得到的结果即为最少的插入次数。动态规划表dp[i]表示从索引i到字符串末尾的子字符串中的最长回文子序列的长度。通过逐个比较字符,如果字符相同,则更新dp表;如果不同,则将dp[j]更新为dp[j]和dp[j-1]中的较大值。

时间复杂度:

O(n^2)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在动态规划的基础上,最终的结果是用字符串的长度减去最长回文子序列的长度得到的?
在将一个字符串转换成回文串时,我们需要插入最少的字符数。一个有效的方法是找到字符串中已经存在的最长回文子序列,因为这部分字符不需要任何插入就已经满足回文的条件。剩余的字符需要通过插入操作来配对,确保整个字符串成为回文。因此,需要插入的字符数等于总字符数减去最长回文子序列的长度,这样就补全了不在回文子序列中的字符,并使整个字符串成为回文。
🦆
在动态规划过程中,temp变量的具体作用是什么?为什么需要使用它来帮助更新dp数组?
在动态规划的过程中,temp变量被用来暂存旧的dp[j]值,在计算新的dp[j]时使用。当s[i]等于s[j]时,我们需要用到i到j-1子字符串的dp值来更新dp[j]。在更新dp[j]之前,temp保存了原始的dp[j],以便在后续循环中使用旧值进行比较和计算。这样做是为了避免在同一轮循环中使用已经更新过的dp值,保证计算的正确性。
🦆
动态规划解法中,为什么将dp数组的每个元素初始化为1而不是0?
在这个问题的动态规划解法中,dp数组的每个元素代表对应索引开始到字符串末尾的最长回文子序列的长度。由于字符串中的任何单个字符都是一个长度为1的回文子序列,因此初始化dp数组的每个元素为1是合理的。这表示即使在最不利的情况下,即从任何索引开始的子字符串至少包含一个字符,即它自身,也是一个有效的回文子序列。
🦆
代码中的嵌套循环结构如何确保能够正确更新dp数组来反映最长回文子序列的长度?
代码中的外层循环从字符串的末尾向前遍历,保证在更新dp[j]时,所有需要参考的dp值(dp[j+1]等)已经计算并存储好了。内层循环则从i开始向字符串末尾遍历,每次比较s[i]和s[j]。如果字符相同,则利用已知的dp值更新当前dp[j],反映从i到j的最长回文子序列长度。如果字符不同,则dp[j]被更新为其自身或其左侧元素dp[j-1]的较大值,确保dp[j]始终保持当前最长的回文子序列长度。这种从后向前的更新保证了在计算每个dp[j]时,所需的所有相关dp值都是最新的,并准确反映了各个子字符串的情况。

相关问题