算法奇妙屋(二十)-回文子串/子序列问题(动态规划)

文章目录

一. 力扣 647. 回文子串

1. 题目解析

2. 算法原理

需要注意的点是填表顺序

3. 代码

java 复制代码
class Solution {
    public int countSubstrings(String ss) {
        // 建表,初始化
        char[] s = ss.toCharArray();
        int n = s.length;
        boolean[][] dp = new boolean[n][n];
        // 填表
        int ret = 0;
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i; j < n; j++) {
                if (s[i] == s[j]) {
                    if (i == j || i + 1 == j) {
                        dp[i][j] = true;
                    }else {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }
                if (dp[i][j]) {
                    ret++;
                }
            }
        }
        return ret;
    }
}

二. 力扣 5. 最长回文子串

1. 题目解析

这里要注意返回的是子串的元素, 而不是长度

2. 算法原理

这道题和(回文子串)基本上原理一致, 只需要加上一步用来求最大长度和起始位置即可

3. 代码

java 复制代码
class Solution {
    public String longestPalindrome(String ss) {
        // 建dp表
        char[] s = ss.toCharArray();
        int n = s.length;
        boolean[][] dp = new boolean[n][n];
        int start = 0;
        int len = 0;
        // 填dp表
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i; j < n; j++) {
                if (s[i] == s[j]) {
                    dp[i][j] = i + 1 < j ? dp[i + 1][j - 1] : true;
                }
                if (dp[i][j]) {
                    if (j - i + 1 > len) {
                        len = j - i + 1;
                        start = i;
                    }
                }
            }
        }
        return ss.substring(start, start + len);
    }
}

三. 力扣 1745. 分割回文串 IV

1. 题目解析

2. 算法原理

这道题和上面的题大体思想不变, 这里我们只简单回顾一下dp表的创建过程, 以及如何利用dp表来做这道题

3. 代码

java 复制代码
class Solution {
    public boolean checkPartitioning(String ss) {
        // 建表
        char[] s = ss.toCharArray();
        int n = s.length;
        boolean[][] dp = new boolean[n][n];
        // 填表
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i; j < n; j++) {
                if (s[i] == s[j]) {
                    dp[i][j] = i + 1 < j ? dp[i + 1][j - 1] : true;
                }
            }
        }
        // 再次遍历dp表, 开始分割
        for (int i = 1; i < n - 1; i++) {
            for (int j = i; j < n - 1; j++) {
                if (i - 1 >= 0 && j + 1 <= n - 1) {
                    if (dp[0][i - 1] && dp[i][j] && dp[j + 1][n - 1]) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
}

四. 力扣 132. 分割回文串 II

1. 题目解析

这道题上面那道题的进阶版本, 思路差不多一致

2. 算法原理

先用二维dp1表来存储每个子串状态, 再用新dp2表来求最小分割次数, 因为求dp1的过程我们已重复很多次, 因此这里省去这个过程

3. 代码

java 复制代码
class Solution {
    public int minCut(String ss) {
        // 建表
        char[] s = ss.toCharArray();
        int n = s.length;
        boolean[][] f = new boolean[n][n];
        int[] dp = new int[n];
        // 初始化
        for (int i = 0; i < n; i++) {
            dp[i] = 0x3f3f3f3f;
        }
        // 填f状态表
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i; j < n; j++) {
                if (s[i] == s[j]) {
                    f[i][j] = i + 1 < j ? f[i + 1][j - 1] : true;
                }
            }
        }
        // 填dp表
        for (int i = 0; i < n; i++) {
            if (f[0][i]) {
                dp[i] = 0;
            }else {
                for (int j = 1; j <= i; j++) {
                    if (f[j][i]) {
                        dp[i] = Math.min(dp[i], dp[j - 1] + 1);
                    }
                }
            }
        }
        return dp[n - 1];
    }
}

五. 力扣 516. 最长回文子序列

1. 题目解析

题干虽短, 但蕴含的信息很多

2. 算法原理

我们这里相当于遍历所有子串, 先求单个字符和两个字符的最大长度,再以此类推求整个范围, dpi,j是范围内所有子序列, 不是单个序列

3. 代码

java 复制代码
class Solution {
    public int longestPalindromeSubseq(String ss) {
        // 建dp表,初始化
        char[] s = ss.toCharArray();
        int n = s.length;
        int[][] dp = new int[n][n];
        // 填dp表
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i; j < n; j++) {
                if (s[i] == s[j]) {
                    if (i == j) {
                        dp[i][j] = 1;
                    }else if (i + 1 == j) {
                        dp[i][j] = 2;
                    }else {
                        dp[i][j] = dp[i + 1][j - 1] + 2;
                    }
                }else {
                    dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
                }
            }
        }
        return dp[0][n - 1];
    }
}

六. 力扣 1312. 让字符串成为回文串的最少插入次数

1. 题目解析

2. 算法原理

这里要注意的是dpi,j代表的含义, 这里代表的是单个子串, 而不是范围内的所有子串

3. 代码

java 复制代码
class Solution {
    public int minInsertions(String ss) {
        // 建表
        char[] s = ss.toCharArray();
        int n = s.length;
        int[][] dp = new int[n][n];
        // 填表
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i + 1; j < n; j++) {
                if (s[i] == s[j]) {
                    if (i + 1 < j) {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }else {
                    dp[i][j] = Math.min(dp[i][j - 1], dp[i + 1][j]) + 1;
                }
            }
        }
        return dp[0][n - 1];
    }
}
相关推荐
潇凝子潇11 小时前
IDEA插件
java·ide·intellij-idea
摇滚侠11 小时前
SSM 框架实战教程 SpringBoot 自动配置 176-179
java·spring boot·后端
JAVA96511 小时前
JAVA面试-JVM篇 02-G1垃圾收集器的工作原理是什么与CMS的区别
java·jvm·面试
ywl47081208712 小时前
spring单列bean之循环依赖核心源码解读
java·后端·spring
我命由我1234512 小时前
RFID 技术极简理解
java·c语言·c++·嵌入式硬件·物联网·visualstudio·java-ee
workflower12 小时前
基于机器学习的设备故障预测分析方法
人工智能·算法·机器学习·设计模式·语言模型·自然语言处理·重构
格发许可优化管理系统12 小时前
Mentor许可证与其他软件许可证的深度比较
java·大数据·运维·c语言·c++·算法
wjcroom12 小时前
时空和电子7-泡力模型含罗量
人工智能·算法·机器学习
pingglala12 小时前
winscp连接linux失败解决方法
java·linux·服务器
Javatutouhouduan12 小时前
深入学习JVM底层原理:源码剖析与实例详解!
java·jvm·java面试·后端开发·java程序员·java八股文·java性能优化