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

文章目录

一. 力扣 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];
    }
}
相关推荐
m沐沐7 小时前
【深度学习】YOLOv2目标检测算法——改进点、网络结构与聚类先验框解析
人工智能·pytorch·深度学习·算法·yolo·目标检测·transformer
不会就选b7 小时前
算法日常・每日刷题--<链表>3
数据结构·算法·链表
geovindu8 小时前
go: Iterative Algorithms
开发语言·后端·算法·golang·迭代算法
前端工作日常9 小时前
我学习到的Java中domain和dto区别
java·后端
码农学院10 小时前
基于Spring Boot的跨境电商多语言订单管理系统架构设计
java·大数据·spring boot
Zachery Pole10 小时前
CCF-CSP备战NO.7【队列】
算法
闪电悠米10 小时前
力扣hot100-48.旋转图像-转置翻转详解
算法·leetcode·职场和发展
技术小结-李爽10 小时前
【工具】pom文件的<packaging>
java·maven
满天星830357711 小时前
【算法】最长递增子序列(三种解法)
算法
l1564694811 小时前
突围!图文混合知识库难以解析,Kimi-K3 原生视觉架构深耕知识工作,DMXAPI 统一接口,缩短项目开发周期
java·大数据·开发语言