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

之前写过一篇最长回文子序列的博客算法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;//二维动态规划表,列数多构建1String[][] 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;//默认回文长度为1int length = 1;//二维动态规划表,列数多构建1boolean[][] 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.rhkb.cn/news/268449.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

汽车大灯尾灯的车灯罩破损破裂裂纹等问题用什么胶可以修复??

汽车大灯尾灯破裂可以使用硅酮玻璃胶或者环氧树脂胶进行修复。 环氧树脂胶的优点主要包括&#xff1a; 粘接力强&#xff1a;环氧树脂胶也具有很高的粘接力&#xff0c;可以有效地将裂缝两侧的材料粘合在一起&#xff0c;确保牢固和持久的修复效果。内聚强度大&#xff1a;环…

为啥要用C艹不用C?

在很多时候&#xff0c;有人会有这样的疑问 ——为什么要用C&#xff1f;C相对于C优势是什么&#xff1f; 最近两年一直在做Linux应用&#xff0c;能明显的感受到C带来到帮助以及快感 之前&#xff0c;我在文章里面提到环形队列 C语言&#xff0c;环形队列 环形队列到底是怎么回…

自学高效备考2025年AMC8数学竞赛:2000-2024年AMC8真题解析

今天继续来随机看五道AMC8的真题和解析&#xff0c;根据实践经验&#xff0c;对于想了解或者加AMC8美国数学竞赛的孩子来说&#xff0c;吃透AMC8历年真题是备考最科学、最有效的方法之一。下面的五道题目如果你能在8分钟内做对&#xff08;主要结果对&#xff0c;无需过程&…

一些C语言知识

C语言的内置类型&#xff1a; char short int long float double C99中引入了bool类型&#xff0c;用来表示真假的变量类型&#xff0c;包含true&#xff0c;false。 这个代码的执行结果是什么&#xff1f;好好想想哦&#xff0c;坑挺多的。 #include <stdio.h>int mai…

观成科技:加密C2框架Covenant流量分析

工具介绍 Covenant是一个基于.NET的开源C2服务器&#xff0c;可以通过HTTP/HTTPS 控制Covenant agent&#xff0c;从而实现对目标的远程控制。Covenant agent在与C2通信时&#xff0c;使用base64/AES加密载荷的HTTP隧道构建加密通道。亦可选择使用SSL/TLS标准加密协议&#xf…

【InternLM 实战营笔记】基于 InternLM 和 LangChain 搭建你的知识库

准备环境 bash /root/share/install_conda_env_internlm_base.sh InternLM升级PIP # 升级pip python -m pip install --upgrade pippip install modelscope1.9.5 pip install transformers4.35.2 pip install streamlit1.24.0 pip install sentencepiece0.1.99 pip install a…

【推荐算法系列十七】:GBDT+LR 排序算法

排序算法经典中的经典 参考 推荐系统之GBDTLR 极客时间 手把手带你搭建推荐系统 课程 逻辑回归&#xff08;LR&#xff09;模型 逻辑回归&#xff08;LR,Logistic Regression&#xff09;是一种传统机器学习分类模型&#xff0c;也是一种比较重要的非线性回归模型&#xff…

js监听网页iframe里面元素变化其实就是监听iframe变化

想要监听网页里面iframe标签内容变化&#xff0c;需要通过监听网页dom元素变化&#xff0c;然后通过查询得到iframe标签&#xff0c;再通过iframe.contentWindow.document得到ifram内的document&#xff0c;然后再使用选择器得到body元素&#xff0c;有了body元素&#xff0c;就…

2024年 前端JavaScript Web APIs 第一天 笔记

1.1 -声明变量const优先 1.2 -DOM树和DOM对象 1.3 -获取DOIM元素 1.4 -DOM修改元素内容以及年会抽奖 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content&quo…

【JAVA重要知识 | 第三篇】深入理解并暴打AQS原理、ReentrantLock锁

文章目录 3.深入理解AQS、ReentrantLock3.1AQS3.1.1AQS简介3.1.2核心结构&#xff08;1&#xff09;设计模型&#xff08;2&#xff09;组成部分&#xff08;3&#xff09;State关键字 3.1.3实现的两类队列&#xff08;1&#xff09;同步队列①CLH②Node③主要行为 img条件队列…

Maven实战(2)之搭建maven私服

一, 背景: 如果使用国外镜像,下载速度比较慢; 如果使用阿里云镜像,速度还算OK,但是假如网速不好的时候,其实也是比较慢的; 如果没有网的情况下更加下载不了. 二, 本地仓库、个人/公司私服、远程仓库关系如下: 三, 下载安装nexus私服 略

【代码】Android|获取压力传感器、屏幕压感数据(大气压、原生和Processing)

首先需要分清自己需要的是大气压还是触摸压力&#xff0c;如果是大气压那么就是TYPE_PRESSURE&#xff0c;可以参考https://source.android.google.cn/docs/core/interaction/sensors/sensor-types?hlzh-cn。如果是触摸压力就是另一回事&#xff0c;我需要的是触摸压力。 不过…

在golang中使用protoc

【Golang】proto生成go的相关文件 推荐个人主页&#xff1a;席万里的个人空间 文章目录 【Golang】proto生成go的相关文件1、查看proto的版本号2、安装protoc-gen-go和protoc-gen-go-grpc3、生成protobuff以及grpc的文件 1、查看proto的版本号 protoc --version2、安装protoc-…

鸿蒙App开发新思路:小程序转App

国家与国家之间错综复杂&#xff0c;在谷歌的安卓操作系统“断供”后&#xff0c;鸿蒙系统的市场化&独立化的道路便显而易见了。 2024年1月18日&#xff0c;华为宣布&#xff0c;不再兼容安卓的“纯血鸿蒙”--HarmonyOS NEXT鸿蒙星河版最终面世&#xff0c;并与2024年Q4正…

2024全国水科技大会暨水环境新污染物控制青年学者论坛

论坛召集人&#xff1a;李江 贵州大学资源与环境工程学院教授 鼓励广大学者不负时代召唤&#xff0c;努力参与到我国的新污染物污染防治、有毒有害化学品环境安全、环境保护和生态守护等领域的理论研究和技术创新之中&#xff0c;并为从事环境及相关学科领域研究的研究生和学者…

Legacy Mirror Shaders and Post Effects

下载&#xff1a;​​Unity资源商店链接资源下载链接 效果图&#xff1a;

快递平台独立版小程序源码|带cps推广营销流量主+前端

源码介绍&#xff1a; 快递代发快递代寄寄件小程序可以对接易达云洋一级总代 快递小程序&#xff0c;接入云洋/易达物流接口&#xff0c;支持选择快递公司&#xff0c;三通一达&#xff0c;极兔&#xff0c;德邦等&#xff0c;功能成熟 如何收益: 1.对接第三方平台成本大约4元…

操作系统(1)——学习导论(Ⅱ)

目录 小程一言专栏链接: [link](http://t.csdnimg.cn/6grrU) 学习导论&#xff08;Ⅱ&#xff09;操作系统-赏前人佳作大型操作系统大型操作系统的一些特点和功能举例 服务器操作系统服务器操作系统特点和功能举例 多处理器操作系统举例 个人计算机操作系统举例 掌上计算机操作…

buildadmin 入口文件index.php的代码解析

buildadmin的入口文件和一般的tp8的入口文件是不一样的&#xff0c;参考这个入口文件的写法&#xff0c;我们可以大至了解&#xff0c; 为什么&#xff0c;前端的 index.html 和 php的入口文件同在 public 的目录下&#xff0c;而可以不冲突 先看一下 buildadmin的入口文件 &l…

WebSocket介绍+3分钟时间使用WebSocket搭建属自己的聊天室

WebSocket 的由来 在 WebSocket 出现之前&#xff0c;我们想实现实时通信、变更推送、服务端消息推送功能&#xff0c;我们一般的方案是使用 Ajax 短轮询、长轮询两种方式&#xff1a;比如我们想实现一个服务端数据变更时&#xff0c;立即通知客户端功能&#xff0c;没有 WebS…