回文子序列问题解题模板

回文子序列问题解题模板及本质


回文子序列问题的本质:

  • 回文子序列是一个动态规划相关的经典问题,其目标是寻找一个序列中的最长回文子序列。
  • 回文子序列的本质特点:
    • 回文的定义:从左到右读与从右到左读一样。
    • 子序列的定义:不用连续,但保持相对位置。
  • 核心思想:
    • 通过状态转移和子问题划分解决问题。
    • 回文的性质通常反映在递归和动态规划的左右扩展关系中。

经典问题及解题模板

1. Leetcode 516. 最长回文子序列

问题描述:

给定一个字符串 s,寻找其中最长的回文子序列的长度。

解法特点:

  • 使用动态规划解决问题,通过判断两端字符是否相等来决定状态转移。
  • 经典的二维动态规划问题。

解题模板:动态规划
核心步骤:
  1. 定义状态:
    • 定义 dp[i][j] 表示字符串 s[i...j] 的最长回文子序列长度。
    • i 表示起始位置,j 表示结束位置。
  2. 状态转移方程:
    • 如果 s[i] == s[j]

      dp\[i\]\[j\] = dp\[i+1\]\[j-1\] + 2

      两端字符可以构成回文的一部分。
    • 如果 s[i] != s[j]

      dp\[i\]\[j\] = \\max(dp\[i+1\]\[j\], dp\[i\]\[j-1\])

      两端字符无法同时参与回文,则选择舍弃一侧。
  3. 初始化:
    • 对于单字符回文:dp[i][i] = 1
    • 对于空区间:dp[i][j] = 0j < i)。
  4. 结果:
    • 答案是 dp[0][n-1],即从字符串的起点到终点的最长回文子序列长度。

代码模板:动态规划
java 复制代码
class Solution {
    public int longestPalindromeSubseq(String s) {
        int n = s.length();
        // 初始化 DP 数组
        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]); // 两端不相等
                }
            }
        }
        
        return dp[0][n - 1]; // 全区间最长回文长度
    }
}

复杂度分析

  1. 时间复杂度:

    • 外层子区间循环需要进行 (O(n^2)) 计算。
    • 每次计算时,只需根据公式访问相关状态。
    • 总时间复杂度为 (O(n^2))。
  2. 空间复杂度:

    • 二维 DP 数组占用 (O(n^2))。

本问题扩展:逻辑与技巧

2. Leetcode 1312. 让字符串成为回文的最少插入次数

问题描述:

给定一个字符串 s,返回将其转化为回文字符串的最少插入次数。

本质和解法:

  • 本质:求一个字符串的"最少插入次数",实际上可以转化为寻找"最长回文子序列"的补集长度。
  • 公式关系:
    最少插入次数 = 字符串长度 - 最长回文子序列的长度。

代码模板:动态规划
java 复制代码
class Solution {
    public int minInsertions(String s) {
        int n = s.length();
        // 这里复用最长回文子序列的 DP 定义
        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]);
                }
            }
        }
        
        return n - dp[0][n - 1]; // 补集长度
    }
}

3. Leetcode 647. 回文子串

问题描述:

给定一个字符串 s,计算该字符串中有多少个回文子串


代码模板:动态规划解决回文子串问题
  • 核心思路:
    通过动态规划记录每个子区间是否是回文。
java 复制代码
class Solution {
    public int countSubstrings(String s) {
        int n = s.length();
        boolean[][] dp = new boolean[n][n]; // 定义 DP 表:dp[i][j] 表示 s[i...j] 是否是回文
        int count = 0; // 记录回文子串的数量
        
        // 遍历子区间长度
        for (int len = 1; len <= n; len++) {
            for (int i = 0; i <= n - len; i++) {
                int j = i + len - 1; // 计算右边界
                
                if (len == 1) { // 单字符回文
                    dp[i][j] = true;
                } else if (len == 2) { // 两个字符,仅限相等情况
                    dp[i][j] = s.charAt(i) == s.charAt(j);
                } else { // 多字符的情况
                    dp[i][j] = s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1];
                }
                
                if (dp[i][j]) {
                    count++; // 如果是回文子串,计数加一
                }
            }
        }
        
        return count; // 返回总数
    }
}

如何快速写出 AC 代码

  1. 明确子问题的递归关系:右边界和子问题依赖;
  2. 初始化边界条件,例如单字符、空区间;
  3. 状态转移的方向是从短区间到长区间,逐步递推;
  4. 边界处理清晰,提升鲁棒性。

典型例题总结

  1. 最长回文子序列问题: Leetcode 516
  2. 最少插入次数: Leetcode 1312
  3. 回文子串数量: Leetcode 647
  4. 问题转化扩展: 通过 DP 模板处理多种回文字符串问题,灵活应用递归关系。

通过动态规划模板可以快速实现并解决回文相关问题,适配多种场景,非常适合面试。

相关推荐
songx_995 分钟前
leetcode(填充每个节点的下一个右侧节点指针 II)
java·数据结构·算法·leetcode
chenyuhao20247 分钟前
vector深度求索(上)实用篇
开发语言·数据结构·c++·后端·算法·类和对象
minstbe1 小时前
半导体数据分析:GPR算法小白入门(三) 晶体管I-V特性仿真教程
算法
未知陨落2 小时前
LeetCode:60.单词搜索
算法·leetcode
mmz12072 小时前
动态规划 练习(c++)
c++·算法·动态规划
tqs_123453 小时前
分sheet写入excel
开发语言·python·算法
西望云天3 小时前
基础组合计数(三道例题)
数据结构·算法·icpc
小灰灰的FPGA4 小时前
29.9元汉堡项目:基于matlab+FPGA的FFT寻峰算法实现
算法·matlab·fpga开发
成成成成成成果4 小时前
软件测试面试八股文:测试技术 10 大核心考点(二)
python·功能测试·测试工具·面试·职场和发展·安全性测试
花心蝴蝶.5 小时前
JVM 垃圾回收
java·jvm·算法