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

2024-03-04 04:36

本文主要是介绍算法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));}
}

这篇关于算法43:动态规划专练(最长回文子串 力扣5题)---范围模型的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/772025

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

动态规划---打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 思路: 动态规划五部曲: 1.确定dp数组及含义 dp数组是一维数组,dp[i]代表

poj2406(连续重复子串)

题意:判断串s是不是str^n,求str的最大长度。 解题思路:kmp可解,后缀数组的倍增算法超时。next[i]表示在第i位匹配失败后,自动跳转到next[i],所以1到next[n]这个串 等于 n-next[n]+1到n这个串。 代码如下; #include<iostream>#include<algorithm>#include<stdio.h>#include<math.