算法43:动态规划专练(最长回文子串 力扣5题)---范围模型

news2024/9/27 23:22:07

之前写过一篇最长回文子序列的博客算法27:最长回文子序列长度(力扣516题)——样本模型 + 范围模型-CSDN博客

在那一篇博客中,回文是可以删除某些字符串组成的。比如:

字符串为:a1b3c4fdcdba, 那么最长回文子序列就是 abccba。长度为6。

本题为力扣第5题:最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

示例 1:

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

示例 2:

输入:s = "cbbd"
输出:"bb"

解释一下,如果字符串为 abc121dmcba. 那么最长回文子序列为 abc121cba. 而最长回文子串则为:121.  子串必须是连续的。

一眼看上去就是范围模型。而范围模型就是要讨论样本数据的开头和结尾的情况:

1. 如果字符串为空,那么回文为空字符

2. 如果字符长度为1, 回文子串就为字符串本身

3. 如果字符串长度2, 则字符串下标0和1的字符进行比较,相等则为字符串本身;不等的话,返回其中一个字符即可。这是我在提交代码的时候,力扣提示错误的时候发现的。

为什么要单独讨论长度为 1 和 2 的情况?

因为, 范围模型讨论数据的开头和结尾。如果原始字符串长度为2,则直接走上方的3逻辑; 可如果一个很长的字符串,经过不断的递归以后,最终长度为2的时候,这就比较麻烦了。

比如 *******ab****的时候,你就不能随意返回一个字符作为回文了。

如果你返回a, 那么字符串为mnfabbbbbbbbb. 那你肯定是错误的

如果你返回b,那么字符串为mnfaaaaaaaaabb, 那你肯定也是错的。

回文,就是整体与子串的关系

其实,最长回文子串,最难的就是连续子串的判断。

012345
acddck

字符串为 acddck, 下标1和下标4相等,都为c.  如果下标从1到4 是回文。 那么他的子串

下标2到3也必须是回文才行。这才是判断的核心点。 而下方的推导表格,完全符合。

比如这个字符串为abdddfm。那么二维表格为:

我用x代表空字符串

a (0)b (1)d (2)d (3)d (4)f (5)m (6)
a (0)aX
b (1)bX
d (2)ddd
d (3)ddd
d (4)dX
f (5)fX
m (6)m

由下往上,由左往右推算:

我用x代表空字符串

a (0)b (1)d (2)d (3)d (4)f (5)m (6)
a (0)aXd 类推 类推 类推 类推
b (1)bX   类推 类推 类推
d (2)ddd

 

 前dd,

左下d,

下为dd

当前下标与下标2的字符相等。下标 2到4的子串为 3到3。 而3行3列是回文并且回文为d。

那么 d + d + d = ddd

 类推 类推
d (3)ddd

 前dd,

左下d,

下为空字符

f不等于下标3的d。

取最长的 dd

 类推
d (4)dXX
f (5)fX
m (6)m

最终的二维表就是

a (0)b (1)d (2)d (3)d (4)f (5)m (6)
a (0)aXbddddddddddd
b (1)bXddddddddddd
d (2)dddddddddddd
d (3)ddddddd
d (4)dXX
f (5)fX
m (6)m

直观的看,最长回文字符就是 ddd.

下面贴出递归代码:

package code04.动态规划专项训练03;

/**
 * 力扣 5 题 : 最长回文子串
 * https://leetcode.cn/problems/longest-palindromic-substring/description/?envType=study-plan-v2&envId=dynamic-programming
 */
public class LongestPalindrome_01 {

    public String longestPalindrome(String s) {

        if (s == null || s.isEmpty()) {
            return "";
        }

        if (s.length() == 1) {
            return s;
        }

        if (s.length() ==  2) {
            return s.charAt(0) == s.charAt(1) ? s : String.valueOf(s.charAt(0));
        }
        char[] ss = s.toCharArray();
        return help(ss, 0, ss.length -1);
    }

    //样本对应模型: 就是从后往前讨论样本数据的末尾下标无限可能。此处的末尾下标应该为0;
    public String help(char[] ss, int index1,  int index2)
    {
        //只有一个字符
        if (index1 == index2) {
            return  String.valueOf(ss[index1]);
        }

        //两个字符
        if (index1 == index2 - 1) {
            String temp = "";
            if (ss[index1] == ss[index2]) {
                temp = String.valueOf(ss[index1]) + String.valueOf(ss[index2]);
            }
            return temp;
        }

        //index2不作为结尾,index作为开头
        String p1 = help(ss, index1, index2 - 1);
        //index2作为结尾,index1不作为开头
        String p2 = help(ss, index1 + 1, index2);
        //index2不作为结尾,index1 不作为开头
        String p3 = help(ss, index1 + 1, index2 - 1);
        //index2作为结尾, index1 作为开头
        String p4 = ss[index1] == ss[index2] ? help(ss, index1 + 1, index2 - 1) : "";
        if (!"".equals(p4) && (index2 - index1 - 1) == p4.length()) {
            p4 =  String.valueOf(ss[index1]) + p4 + String.valueOf(ss[index2]);
        }

        String result =  p1.length() > p2.length() ? p1 : p2;
        result = result.length() > p3.length() ? result : p3;
        result = result.length() > p4.length() ? result : p4;

        return result;
    }

    public static void main(String[] args) {

        //String s= "bab";
        //String s= "babad";
        //String s = "ac";
        //String s= "cbbd";
        //String s= "abdka";
        String s= "aacabdkacaa";
        LongestPalindrome_01 ss = new LongestPalindrome_01();
        System.out.println(ss.longestPalindrome(s));
    }
}

动态规划:

package code04.动态规划专项训练03;

/**
 * 力扣 5 题 : 最长回文子串
 * https://leetcode.cn/problems/longest-palindromic-substring/description/?envType=study-plan-v2&envId=dynamic-programming
 */
public class LongestPalindrome_01_opt {

    public String longestPalindrome(String s) {

        if (s == null || s.isEmpty()) {
            return "";
        }

        if (s.length() == 1) {
            return s;
        }

        if (s.length() == 2) {
            return s.charAt(0) == s.charAt(1) ? s : s.substring(0,1);
        }

        char[] ss = s.toCharArray();

        int size = ss.length;
        //二维动态规划表,列数多构建1
        String[][] dp = new String[size][size];
        //构建dp的斜线
        for (int i = 0; i < s.length() - 1; i++) {
            //只构建斜线上方部分. 由递归的if (index1 == index2) 得到
            dp[i][i] = String.valueOf(ss[i]);
            //由递归的if (index1 == index2 - 1)得到。递归中还特出判断了length == 2 即原始数组长度为2的
            //情况。但是,动态规划中原始数组长度为2在上方代码已经判断过了。因此,此处只需要关注通用逻辑即可
            dp[i][i+1] =  ss[i] == ss[i + 1] ? String.valueOf(ss[i]) + String.valueOf(ss[i+1]) : "";
        }
        //最后一行最后一列比较特殊,会出现数组越界,因此单独构造
        dp[size - 1][size - 1] = String.valueOf(ss[size - 1]);

        //行,从倒数第3行开始,由下放上推; 因为倒数第1、2行上方代码已经推算出来了
        for (int index1 = size - 3; index1 >= 0; index1--) {
            //列,由左往右推。 这个地方的 index2 = index1 + 2需要看图理解
            for (int index2 = index1 + 2; index2 < size; index2++) {

                //index2不作为结尾,index作为开头
                String p1 = dp[index1][index2 - 1];
                //index2作为结尾,index1不作为开头
                String p2 = dp[index1 + 1][index2];
                //index2不作为结尾,index1 不作为开头
                String p3 = dp[index1 + 1][index2 - 1] != null ? dp[index1 + 1][index2 - 1] : "";

                //index2作为结尾, index1 作为开头
                String p4 = ss[index1] == ss[index2] ? dp[index1 + 1][index2 - 1] : "";
                //特殊处理一下p4为null的情况
                p4 = p4 == null ? "" : p4;
                if (!"".equals(p4) && (index2 - index1 - 1) == p4.length()) {
                    p4 =  String.valueOf(ss[index1]) + p4 + String.valueOf(ss[index2]);
                }

                String result =  p1.length() > p2.length() ? p1 : p2;
                result = result.length() > p3.length() ? result : p3;
                result = result.length() > p4.length() ? result : p4;

                dp[index1][index2] = result;
            }
        }
        return dp[0][size -1];
    }



    public static void main(String[] args) {

        //String s= "bab";
        //String s= "babad";
        //String s = "ac";
        //String s= "cbbd";
        //String s= "abdka";
        String s= "aacabdkacaa";
        //String s= "abbcccbbbcaaccbababcbcabca";
        LongestPalindrome_01_opt ss = new LongestPalindrome_01_opt();
        System.out.println(ss.longestPalindrome(s));
    }
}

测试结果:

测试结果很不理想。速度慢,而且还吃内存,吃内存的主要原因就是动态规划的二维表是字符串类型的。

看了看力扣官方的思想,确实相当不错。下面直接说一下官方的解题思路

1. 官方并不存储字符串,而是存一个flag,标记回文范围.

a (0)b (1)d (2)d (3)d (4)f (5)m (6)
a (0)truefalse
b (1)truefalse
d (2)truetrue
d (3)truetrue
d (4)truefalse
f (5)truefalse
m (6)true

力扣官方定义了一个最长回文子串的开始位置,beginIndex,长度length

从倒数第3行开始,依旧是由下往上,由左往右推算

a (0)b (1)d (2)d (3)d (4)f (5)m (6)
a (0)truefalsefalsefalsefalsefalsefalse
b (1)truefalsefalsefalsefalsefalse
d (2)truetrue

d == d,并且

子串 3行3列也是回文

整体是回文。

开始位置为2,

长度为3

falsefalse
d (3)truetrue

d != f

false

m != d

false

d (4)truefalse

d != m

false

f (5)truefalse
m (6)true

最后,就是根据上方的推算结果进行字符串截图。知道了开始位置,截取字符的长度,问题自然就解决了。

代码如下:

package code04.动态规划专项训练03;

/**
 * 力扣 5 题 : 最长回文子串
 * https://leetcode.cn/problems/longest-palindromic-substring/description/?envType=study-plan-v2&envId=dynamic-programming
 */
public class LongestPalindrome_01_opt2_1 {

    public String longestPalindrome(String s) {

        if (s == null || s.isEmpty()) {
            return "";
        }

        if (s.length() == 1) {
            return s;
        }

        if (s.length() == 2) {
            return s.charAt(0) == s.charAt(1) ? s : s.substring(0,1);
        }

        char[] ss = s.toCharArray();
        int size = ss.length;


        //默认开始下标为最后一行的最后一列
        int beginIndex = size -1;
        //默认回文长度为1
        int length = 1;

        //二维动态规划表,列数多构建1
        boolean[][] dp = new boolean[size][size];
        //构建dp的斜线
        for (int i = 0; i < s.length(); i++) {
            //只构建斜线上方部分. 由递归的if (index1 == index2) 得到
            dp[i][i] = true;
        }

        //行,从倒数第2行开始,由下放上推; 因为倒数第1行上方代码已经推算出来了
        for (int index1 = size - 2; index1 >= 0; index1--) {
            //列,由左往右推。 当前行的剩余列
            for (int index2 = index1 + 1; index2 < size; index2++) {

                //长度为2. 开头、结尾相等就是回文
                if (index1 == index2 - 1) {
                    //开头、结尾相等。那么 [index1, index2] 就是回文
                    dp[index1][index2] =  ss[index1] == ss[index2] ? true : false;
                }
                else {
                    dp[index1][index2] =  ss[index1] == ss[index2] ? dp[index1 + 1][index2 -1] : false;
                }
                // [index1, index2] 的个数就是 index2 - index1 + 1;
                if( dp[index1][index2] && index2 - index1 + 1 > length) {
                    beginIndex = index1;
                    length = index2 - index1 + 1;
                }
            }
        }

        return s.substring(beginIndex, beginIndex + length);
    }



    public static void main(String[] args) {


        //String s= "bab";
         //String s= "babad";
        //String s = "ac";
         String s= "cbbd";
        //String s= "abdka";
         //String s= "aacabdkacaa";

        //String s= "abbcccbbbcaaccbababcbcabca";
        LongestPalindrome_01_opt2_1 ss = new LongestPalindrome_01_opt2_1();
        System.out.println(ss.longestPalindrome(s));
    }
}

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

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

相关文章

赵文彬将出席无磷锅炉工艺助剂在锅炉水节水节能应用

演讲嘉宾&#xff1a;赵文彬 集团副总/技术总监 上远未来水务集团有限公司 演讲题目&#xff1a;无磷锅炉工艺助剂在锅炉水节水节能方面的应用 会议简介 “十四五”规划中提出&#xff0c;提高工业、能源领城智能化与信息化融合&#xff0c;明确“低碳经济”新的战略目标&am…

c++之旅——第四弹

大家好啊&#xff0c;这里是c之旅第三弹&#xff0c;跟随我的步伐来开始这一篇的学习吧&#xff01; 如果有知识性错误&#xff0c;欢迎各位指正&#xff01;&#xff01;一起加油&#xff01;&#xff01; 创作不易&#xff0c;希望大家多多支持哦&#xff01; 本篇文章的主…

一些C语言题目

求10个整数中最大值 #include <stdio.h>//求10个整数中最大值 int main() {int arr[10]{2,5,8,6,19,1,7,3,11,3};int i 0;int max 0;/*for(i 0;i < 10;i){scanf("%d",&arr[i]);}*/for(i 0;i < 10;i){if(arr[i] > max)max arr[i];}printf(&q…

tomcat 反向代理 自建博客 修改状态页 等

一 自建博客 随后&#xff0c;拷贝到webapps下面 并且做软连接 随后重定向 并且下载 cat >/etc/yum.repos.d/mysql.repo <<EOF [mysql57-community] nameMySQL 5.7 Community Server baseurlhttp://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/ enabled1 g…

分享一款我自己开发的自动更新小工具

我们公司最近需要开发一款自动上传的工具&#xff0c;这个工具需要安装在用户电脑上&#xff0c;但是这样不利于维护&#xff0c;于是想到了自动更新这个功能&#xff0c;需要在打开工具时顺带打开自动更新的小工具&#xff0c;这样我们在更新代码后&#xff0c;用户那边就能自…

IDEA POM文件配置profile实现不同环境切换

目录 一、背景 二、实现 2.1创建不同的配置文件 2.2配置POM文件 三、效果 3.1本地使用 2.2线上或者测试环境使用 一、背景 在企业级开发中&#xff0c;为了不影响生产环境的项目运行&#xff0c;一般情况下都会划分生产环境、测试环境、开发环境。不同环境可以配置不同的…

4. 编写app组件

1. 代码 main.ts // 引入createApp用于创建应用 import {createApp} from "vue"// 引入App根组件 import App from ./App.vue createApp(App).mount(#app) App.vue <!-- vue文件可以写三种标签1. template标签&#xff0c;写html结构2. script 脚本标签&…

Linux设备模型(十) - bus/device/device_driver/class

四&#xff0c;驱动的注册 1&#xff0c;struct device_driver结构体 /** * struct device_driver - The basic device driver structure * name: Name of the device driver. * bus: The bus which the device of this driver belongs to. * owner: The module own…

js 面试 什么是WebSockets?HTTP和HTTPS有什么不同?web worker是什么?

概念&#xff1a; webSocket 是一种在客户端和服务端之间建立持久连接的协议&#xff0c;它提供全双工通信通道&#xff0c;是服务器可以主动向客户端推送数据&#xff0c;同时也可以接受客户端发送的数据。 1 webSocket与https区别&#xff1f; 在网络通信中&#xff0c;We…

一款汇聚 精美UI+AI内容生成助手 的实用白板工具

大家好&#xff0c;我是Mandy。今天给大家分享的内容是&#xff0c;如何利用AI快速生成思维导图、PPT、绘画等功能&#xff0c;本文分享的AI功能是基于boardmix实现。 boardmix是一款非常精美的在线白板工具&#xff0c;是一个实时协作的智慧白板上、一键生成PPT、用AI协助创作…

YOLOv9保姆教程,手把手教你训练、检测,快来学习吧!!

首先在这里推送一下我的YOLOv9改进专栏&#xff0c;目前是全网最快的YOLOv9改进专栏&#xff0c;该专栏将更新最新的模块来改进YOLOv9&#xff0c;助力大家论文与科研&#xff0c;欢迎大家了解&#xff01; ⭐专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&…

Python+Selenium4 Web自动化测试框架学习(一)

主要框架及技术 1.第一个selenium例子 import timefrom selenium import webdriver from selenium.webdriver.common.by import Bydriver webdriver.Chrome() driver.get("https://www.bilibili.com") driver.find_element(By.CLASS_NAME,"nav-search-input&…

求阶乘。。

&#xff01;&#xff01;&#xff01;答案解释摘录自蓝桥云课题解 问题描述 满足N!的末尾恰好有个0的最小的N是多少? 如果这样的N不存在输出-1。 输入格式 一个整数 K 输出格式 一个整数代表答案 样例输入 2 样例输出 10 import os import sys# 请在此输入您的代码 def coun…

css通过calc动态计算宽度

max-width: calc(100% - 40px) .m-mj-status-drawing-info-data{ display: inline-block; margin: 10px; min-width: 200px; padding: 10px;border-radius: 10px; background: #ddd;max-width: calc(100% - 40px);word-wrap: break-word;white-space: pre-line;}我开发的chatg…

从第一原理看大语言模型

大模型基础框架 大模型幻觉问题 大模型能力 思维链模式 思维链模式激发的是大模型的推理能力 LLM知识能力RAG

【java-面试题】链表刷题

【java-面试题】链表刷题 1. 删除链表中等于给定值 val 的所有节点&#xff08;最多遍历链表一遍&#xff09;题目思路代码 2. 反转一个单链表&#xff08;就地反转&#xff09; 1. 删除链表中等于给定值 val 的所有节点&#xff08;最多遍历链表一遍&#xff09; 力扣链接&am…

仅仅4M!无缝、快速、安全的效率提升工具

在数字化的今天&#xff0c;文件传输已经成为我们日常生活中不可或缺的一部分。无论是工作汇报的PPT&#xff0c;还是那些珍贵的高清照片&#xff0c;我们都需要一个既快速又安全的方式去分享它们的同时&#xff0c;不受终端设备的影响&#xff0c;我们希望接收方不需要下载任何…

C++内存模型与内存序

写在前面 在真正了解Memory Order的作用之前&#xff0c;曾经简单地将Memory Order等同于mutex和atomic来进行线程间数据同步&#xff0c;或者用来限制线程间的执行顺序&#xff0c;其实这是一个错误的理解。直到后来仔细研究了Memory Order之后&#xff0c;才发现无论是功能还…

一文了解docker与k8s

随着 k8s 作为容器编排解决方案变得越来越流行&#xff0c;有些人开始拿 Docker 和 k8s 进行对比&#xff0c;不禁问道&#xff1a;Docker 不香吗&#xff1f; k8s 是 kubernetes 的缩写&#xff0c;8 代表中间的八个字符。 其实 Docker 和 k8s 并非直接的竞争对手两者相互依存…

前端学习第一天-html基础

达标要求 网页的形成过程 常用的浏览器及常见的浏览器内核 web 标准三层组成 什么是HTML 熟练掌握HTML文档结构 熟练掌握HTML常用标签 1. 初识web前端 Web前端是创建Web页面或App等前端界面呈现给用户的过程。 Web前端开发是从网页制作演变而来&#xff0c;早期网站主…