算法系列--动态规划--回文子串系列

news2024/9/24 21:21:36

💕"我们好像在池塘的水底,从一个月亮走向另一个月亮。。"💕
作者:Mylvzi
文章主要内容:算法系列–动态规划–回文子串系列
在这里插入图片描述

今天为大家带来的是算法系列--动态规划--回文子串系列(1),本文重点掌握如何快速判断一个字符串是否是回文子串

一.回文子串(引入)

先来看力扣上这道题目:回文子串

在这里插入图片描述

1.利用动态规划的思想解决这道题目(重点)

回文子串其实是子数组的一种,只不过这里将数字换为字符而已,所以回文子串的问题也可以使用动态规划的思想解决,但是这个状态表示相较于常规的字符数组有些不同,在回文子串问题中,我们需要创建一个二维的dp表去存储所有子串是否是回文子串的信息,也就是说这个dp表是一个`boolean``类型的数组

首先如何得到所有的子字符串呢?很简单,两层for循环就能解决这个问题
在这里插入图片描述
注意:单独一个字符也算子字符串!!!

明确上述前置知识之后,下面讲解如何利用动态规划的思想解决回文子串问题

  1. 状态表示:dp[i][j]表示以i下标为起始位置,j下标为结束位置的子字符串是否是回文子串的信息

  2. 状态转移方程:
    在这里插入图片描述

  3. 初始化:无需初始化,越界的条件被特别判断了,不会出现越界的情况
    在这里插入图片描述

  4. 填表顺序:从下往上填
    在这里插入图片描述

  5. 返回值:dp[i][j]为true的数目

代码实现:

class Solution {
    public int countSubstrings(String s) {
        char[] ss = s.toCharArray();// 转化为字符数组
        int ret = 0;// 记录dp表中true的数目

        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(ss[i] == ss[j])// 只用判断相等的情况,不相等就是默认值false;
                    dp[i][j] = i + 1 < j ? dp[i + 1][j - 1] : true;
                ret += dp[i][j] == true ? 1 : 0;
            }
        }

        return ret;
    }
}

如果使用动态规划,时间复杂度,空间复杂度均为为O(N^2)回文子串问题使用动态规划虽然不是最优解,但是可以实现一个非常重要的功能将所有子串是否是回文子串的信息,存储到dp表之中,最优解还有中心拓展算法马拉车算法

中心拓展算法的实现:

链接:https://leetcode.cn/problems/palindromic-substrings/solutions/379987/hui-wen-zi-chuan-by-leetcode-solution/
来源:力扣(LeetCode)

class Solution {
    public int countSubstrings(String s) {
        int n = s.length(), ans = 0;
        for (int i = 0; i < 2 * n - 1; ++i) {
            int l = i / 2, r = i / 2 + i % 2;
            while (l >= 0 && r < n && s.charAt(l) == s.charAt(r)) {
                --l;
                ++r;
                ++ans;
            }
        }
        return ans;
    }
}

2.最长回文子串

链接:
https://leetcode.cn/problems/longest-palindromic-substring/

分析:

还是利用和上道题一样的动态规划思想,在本题中需要得到的是最长的回文子串,在回文子串的动态规划里,我们已经保存了所有的回文子串的信息,只要判断出来时回文子串,就更新一下长度即可

可以使用一个数组ret来记录字符串的起始结束位置

代码:

class Solution {
    public String longestPalindrome(String s) {
        char[] ss = s.toCharArray();
        int n = s.length();
        boolean[][] dp = new boolean[n][n];// 创建dp表

        int[] ret = new int[2];// 记录字符串的起始位置和结束位置

        for(int i = n - 1; i >=0; i --){
            for(int j = i; j < n; j++) {
                if(ss[i] == ss[j])
                    dp[i][j] = i + 1 < j ? dp[i + 1][j - 1] : true;
                if(dp[i][j] == true) {
                    if(j - i > ret[1] - ret[0]) {
                        ret[0] = i;ret[1] = j;
                    }
                }
            }
        }

        return s.substring(ret[0],ret[1] + 1);// 注意是"左闭右开"
    }
}

3.回⽂串分割IV

链接:
https://leetcode.cn/problems/palindrome-partitioning-iv/description/
在这里插入图片描述

分析:

  • 其实题目的意思很简单,就是判断字符串s能否分割为三个回文子串,最直观的想法就是暴力求解+判断是否是回文子串,而判断是否是回文子串已经在上面做过了
    在这里插入图片描述
    代码:
class Solution {
    public boolean checkPartitioning(String s) {
        char[] ss = s.toCharArray();// 转化为字符数组
        int ret = 0;// 记录dp表中true的数目

        int n = s.length();
        boolean[][] dp = new boolean[n][n];

        // 使用dp表保存所有的子字符串的信息
        for(int i = n - 1; i >= 0; i--) {// 从后往前遍历字符串
            for(int j = i; j < n; j++) {
                if(ss[i] == ss[j])// 只用判断相等的情况,不相等就是默认值false;
                    dp[i][j] = i + 1 < j ? dp[i + 1][j - 1] : true;
            }
        }

        // 将字符串s分割为三个子字符串,分别判断是否是回文字符串
        for(int i = 1; i < n - 1; i++) {
            for(int j = i; j < n - 1; j++) {
                if(dp[0][i - 1] && dp[i][j] && dp[j + 1][n - 1])
                    return true;
            }
        }

        return false;
    }
}

4.分割回⽂串II

链接:
https://leetcode.cn/problems/palindrome-partitioning-ii/description/
在这里插入图片描述
分析:

其实这道题和单词拆分很像,单词拆分中需要我们遍历整个字符串,判断对应的单词是否存在于字典之中,本题也是需要遍历整个字符串,判断对应的子字符串是否是回文子串,而判断是否是回文子串已经在上面介绍过

在这里插入图片描述

代码:

class Solution {
    public int minCut(String s) {
        char[] ss = s.toCharArray();// 转化为字符数组
        int ret = 0;// 记录dp表中true的数目

        int n = s.length();
        boolean[][] predp = new boolean[n][n];

        for(int i = n - 1; i >= 0; i--) {// 从后往前遍历字符串
            for(int j = i; j < n; j++) {
                if(ss[i] == ss[j])// 只用判断相等的情况,不相等就是默认值false;
                    predp[i][j] = i + 1 < j ? predp[i + 1][j - 1] : true;
            }
        }

        // 下面是正题
        int[] dp = new int[n];
        for(int i = 0; i < n; i++) dp[i] = Integer.MAX_VALUE;// 初始化为最大值

        for(int i = 0; i < n; i++) {
            if(predp[0][i] == true) dp[i] = 0;// 0->i为回文串
            else {// 0->i不是回文串
                for(int j = 1; j <= i; j++) {
                    if(predp[j][i]) dp[i] = Math.min(dp[i],dp[j - 1] + 1);
                }
            }
        }

        return dp[n - 1];
    }
}

5.最长回文子序列

链接:
https://leetcode.cn/problems/longest-palindromic-subsequence/

分析:

最先想到的状态表示就是以i位置为结尾字符串中的最长的回文子序列的长度,但是进一步分析发现此状态表示无法推导出状态转移方程,原因在于我们根本不能确定回文子序列,所以要更换一个状态表示

经过上述分析发现仅仅固定一个位置去表示字符串无法确定其回文子序列,所以需要两个下标来确定一个字符串(是不是和回文子串很像?),然后再去推导状态转移方程,只不过这里的状态相较于连续的子串更多一些,下面是详细的分析过程
在这里插入图片描述

代码:

class Solution {
    public int longestPalindromeSubseq(String s) {

        char[] ss = s.toCharArray();// 转化为字符数组
        int n = s.length();

        // 创建dp表
        int[][] dp = new int[n][n];
        dp[0][0] = dp[n - 1][n - 1] = 1;// 初始化
        int ret = 0;// 记录最大值

        for(int i = n -1; i >=0; i--) {
            for(int j = i; j < n; j++) {
                if(ss[i] == ss[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 + 1][j],dp[i][j - 1]);
                }

                ret = ret > dp[i][j] ? ret : dp[i][j];// 更新最值
            }
        }

        return ret;
    }
}

6.让字符串成为回⽂串的最⼩插⼊次数(hard)

链接:
https://leetcode.cn/problems/minimum-insertion-steps-to-make-a-string-palindrome/description/

分析:

在这里插入图片描述

代码:

class Solution {
    public int minInsertions(String s) {
        char[] ss = s.toCharArray();// 转化为字符数组
        int n = s.length();

        // 创建dp表
        int[][] dp = new int[n][n];

        for(int i = n - 1; i >= 0; i--) {
            for(int j = i; j < n; j++) {
                if(ss[i] == ss[j]) dp[i][j] = i + 1 < j ? dp[i + 1][j - 1] : 0;
                else dp[i][j] = Math.min(dp[i + 1][j],dp[i][j - 1]) + 1;
            }
        }

        return dp[0][n - 1];
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1539509.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

树,二叉树与堆

这里写目录标题 树树的概念树的相关概念树的表示 二叉树二叉树的概念满二叉树与完全二叉树二叉树的重要性质二叉树的存储结构 堆二叉树的顺序存储堆的概念堆的实现堆插入和删除数据 树 树的概念 树的概念&#xff1a; 树是一种非线性的数据结构&#xff0c;它是由n&#xff08…

链表oj测试题(上)

链表的申明&#xff1a; struct ListNode {int val;struct ListNode* next; }; 1.题1 删除指定元素 例如&#xff1a;链表1 2 6 3 4 5 6&#xff0c;然后选择删除元素6&#xff0c;返回的链表为1 2 3 4 5 。 代码演示&#xff1a; typedef struct ListNode ListNode;List…

01-java面试题八股文-----java基础——20题

文章目录 <font color"red">1、java语言有哪些特点&#xff1a;<font color"red">2、面向对象和面向过程的区别<font color"red">3、标识符的命名规则。<font color"red">4、八种基本数据类型的大小&#xff…

Elastic 线下 Meetup 将于 2024 年 3 月 30 号在武汉举办

2024 Elastic Meetup 武汉站活动&#xff0c;由 Elastic、腾讯、新智锦绣联合举办&#xff0c;现诚邀广大技术爱好者及开发者参加。 活动时间 2024年3月30日 13:30-18:00 活动地点 中国武汉 武汉市江夏区腾讯大道1号腾讯武汉研发中心一楼多功能厅 13:30-14:00 入场 活动流程…

关于调度算法,小林给出更好的例子(银行办理业务)

看的迷迷糊糊&#xff1f;那我拿去银行办业务的例子&#xff0c;把上面的调度算法串起来&#xff0c;你还不懂&#xff0c;你锤我&#xff01; 办理业务的客户相当于进程&#xff0c;银行窗口工作人员相当于 CPU。 现在&#xff0c;假设这个银行只有一个窗口&#xff08;单核 …

Prometheus Grafana 配置仪表板

#grafana# 其实grafana提供了丰富的Prometheus数据源的仪表板&#xff0c;基本上主流的都有&#xff0c;通过下面官方地址可查阅 Dashboards | Grafana Labs 这里举例说明&#xff0c;配置node_exporter仪表板 首先&#xff0c;在上面的网站搜索 node 可以查到蛮多的仪表板…

使用 Pytorch 和 Rasterio 的自定义地理空间数据加载器

地理空间数据在从遥感和城市规划到环境监测和灾害管理的各个领域发挥着至关重要的作用。在处理机器学习任务的地理空间数据时,准备自定义数据加载器对于有效加载、预处理和增强数据而不丢失其属性至关重要,特别是当输入图像具有超过 3 个波段时。 Rasterio确实是一个专门为有…

FPGA——DDR3的IP核

FPGA——DDR3的ip核 IP核配置基于MIG核代码基于AXI接口的DDR3 IP核配置 1 2 3 4 5 6 基于MIG核代码 控制MIG核的信号进行读写 module MIG_APP_Drive(input i_ui_clk ,input i_ui_rst ,input init_calib_…

SpringCloud Alibaba Nacos 服务注册和配置中心

一、前言 接下来是开展一系列的 SpringCloud 的学习之旅&#xff0c;从传统的模块之间调用&#xff0c;一步步的升级为 SpringCloud 模块之间的调用&#xff0c;此篇文章为第十二篇&#xff0c;即介绍 SpringCloud Alibaba Nacos 服务注册和配置中心。 二、Nacos 简介 2.1 为…

【DataWhale】灵境Agent开发——低代码创建AI智能体

灵境Agent开发——低代码创建AI智能体 3 灵境 Agent 低代码开发 ​ 低代码模式支持开发者通过编排工作流的方式快速构建智能体&#xff0c;您可以通过拖拽和组合模型、提示词、代码等模块&#xff0c;实现准确的、复杂的业务流程。 ​ 个人体验下来&#xff0c;目前这个低代…

华为OD机22道试题

华为OD机试题 2.查找小朋友的好朋友位置 在学校中&#xff0c;N 个小朋友站成一队&#xff0c;第 i 个小朋友的身高为 height[i]&#xff0c;第 i 个小朋友可以看到第一个比自己身高更高的小朋友j&#xff0c;那么 j 是 i 的好朋友 (要求&#xff1a;j>i) 。 请重新生成一个…

[运维] 可视化爬虫易采集-EasySpider(笔记)

一、下载 ​下载地址 下滑到Assets页面&#xff0c;选择下载 二、解压运 ​解压压缩包&#xff0c;打开文件夹 在此文件夹下打开Linux Terimal, 并输入以下命令运行软件&#xff1a; ./easy-spider.sh 注意软件运行过程中不要关闭terminal。 三、使用 1.开始 首先点击…

机器学习算法那些事 | 数据算法工程师必须掌握的5个Python库

本文来源公众号“OpenCV与AI深度学习”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;数据算法工程师必须掌握的5个Python库 如果你是一名初学者或中级机器学习工程师或数据科学家&#xff0c;这篇文章非常适合你。你已经选择了自…

【C语言】动态内存分配

1、为什么要有动态内存分配 不管是C还是C中都会大量的使用&#xff0c;使用C/C实现数据结构的时候&#xff0c;也会使用动态内存管理。 我们已经掌握的内存开辟方式有&#xff1a; int val 20; //在栈空间上开辟四个字节 char arr[10] { 0 }; //在栈空间…

[SAP ABAP] SE11查询数据库表中的数据

我们可以通过事务码SE11查询对应数据库表中的详细数据 本次查询使用的数据库表名为MARA&#xff0c;具体操作如下所示: ① 输入事务码SE11进入ABAP字典操作界面&#xff0c;在数据库表搜索框中输入目标表名MARA&#xff0c;并点击【显示】按钮 ② 进入到显示表界面&#xff0…

阿里云服务器租用一年多少钱?2024年最新阿里云租用价格

2024年阿里云服务器租用费用&#xff0c;云服务器ECS经济型e实例2核2G、3M固定带宽99元一年&#xff0c;轻量应用服务器2核2G3M带宽轻量服务器一年61元&#xff0c;ECS u1服务器2核4G5M固定带宽199元一年&#xff0c;2核4G4M带宽轻量服务器一年165元12个月&#xff0c;2核4G服务…

HarmonyOS NEXT应用开发—使用绘制组件实现自定义进度动画

介绍 本示例介绍使用绘制组件中的Circle组件以及Path组件实现实时进度效果。该场景多用于手机电池电量、汽车油量、水位变化等动态变化中。 效果预览图 使用说明 加载完成后初始显示进度为0%&#xff0c;颜色为红色&#xff0c;且有充电、放电两个按钮。点击充电按钮&#x…

C++中的std::for_each并行执行探索

在C标准库中&#xff0c;std::for_each是一个用于遍历容器或可迭代序列并对每个元素执行特定操作的强大工具。传统的std::for_each是顺序执行的&#xff0c;即它会按照元素在序列中的顺序&#xff0c;逐个应用函数对象或lambda表达式。然而&#xff0c;随着多线程编程的普及和硬…

升级 HarmonyOS 4 版本,腕上智慧更进一步

HUAWEI WATCH GT 3 系列升级 HarmonyOS 4 新版本后&#xff0c;手表体验更进一步&#xff0c;快来看看有哪些变化吧~

Vue2(八):TodoList案例

一、整体思路 1.分析结构 我们对大盒子拆分&#xff0c;分成header、list、footer&#xff0c;但是list最好也进行拆分&#xff0c;因为它里面的每个小盒子结构一样就是字不一样&#xff0c;可以用一个组件多次调用完成&#xff0c;所以分成app>header、list、footer>i…