回文子序列问题解题模板

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


回文子序列问题的本质:

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

经典问题及解题模板

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 模板处理多种回文字符串问题,灵活应用递归关系。

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

相关推荐
Doopny@10 分钟前
数字组合(信息学奥赛一本通-1291)
数据结构·算法·动态规划
原来是猿1 小时前
蓝桥备赛(13)- 链表和 list(上)
开发语言·数据结构·c++·算法·链表·list
项目申报小狂人1 小时前
高性能算法NGO!北方苍鹰优化算法(Northern Goshawk Optimization,NGO)
算法·数学建模
且听风吟ayan1 小时前
leetcode day26 重复的子字符串
算法·leetcode·c#
仟濹1 小时前
【算法 C/C++】二维差分
c语言·c++·算法
*星星之火*2 小时前
【GPT入门】第9课 思维树概念与原理
gpt·算法·深度优先
海姐软件测试2 小时前
面试时,如何回答好“你是怎么测试接口的?”
测试工具·面试·职场和发展·postman
总斯霖2 小时前
题解:士兵排列
数据结构·c++·算法
稳兽龙2 小时前
P4268 [USACO18FEB] Directory Traversal G
c++·算法·换根dp
我是大咖3 小时前
c语言笔记 一维数组与二维数组
c语言·笔记·算法