代码随想录|647. 回文子串,516.最长回文子序列

news/2024/12/4 19:29:44/

647. 回文子串

1.dp含义

dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是,则dp[i][j]为true,否则为false。

2.dp递推公式

整体上是两种,就是s[i]与s[j]相等,s[i]与s[j]不相等这两种。

当s[i]与s[j]不相等,那没啥好说的了,dp[i][j]一定是false。

当s[i]与s[j]相等时,这就复杂一些了,有如下三种情况

  • 情况一:下标i 与 j相同,同一个字符例如a,当然是回文子串
  • 情况二:下标i 与 j相差为1,例如aa,也是回文子串
  • 情况三:下标:i 与 j相差大于1的时候,例如cabac,此时s[i]与s[j]已经相同了,我们看i到j区间是不是回文子串就看aba是不是回文就可以了,那么aba的区间就是 i+1 与 j-1区间,这个区间是不是回文就看dp[i + 1][j - 1]是否为true。

3.dp初始化

dp初始化为false

4.遍历顺序

要从前往后遍历,为什么呢,首先从递推公式中可以看出,情况三是根据dp[i + 1][j - 1]是否为true,再对dp[i][j]进行赋值true的。

dp[i + 1][j - 1] 在 dp[i][j]的左下角,如图:

所以我们应该从后往前遍历字符串

5.推导dp

举例,输入:"aaa",dp[i][j]状态如下:

图中有6个true,所以就是有6个回文子串。

注意因为dp[i][j]的定义,所以j一定是大于等于i的(从遍历顺序也可以看出来),那么在填充dp[i][j]的时候一定是只填充右上半部分

代码实现

class Solution {public int countSubstrings(String s) {int m=s.length();//dp含义:dp[i][j]表示区间范围[i][j]的子串是否是回文子串,如果是,那么dp[i][j]为true,否则为falseboolean[][] dp=new boolean[m][m];int result=0;for(int i=m-1;i>=0;i--){//注意倒序遍历for(int j=i;j<m;j++){if(s.charAt(i)==s.charAt(j)){if(j-i<=1){//情况1和2result++;dp[i][j]=true;}else if(dp[i+1][j-1]){//情况三result++;dp[i][j]=true;}}//如果s.charAt(i)!=s.charAt(j),那么dp[i][j]为false}        }return result;}
}

516.最长回文子序列

回文子串是要连续的,回文子序列可不是连续的!

1.dp含义

dp[i][j]:字符串s在[i,j]范围内最长的回文子序列的长度为dp[i][j].

2.确定递推公式

如果s[i]与s[j]相同,那么dp[i][j]=dp[i+1][j-1]+2;

如果s[i]与s[j]不相同,说明s[i]和s[j]的同时加入 并不能增加[i,j]区间回文子序列的长度,那么分别加入s[i]、s[j]看看哪一个可以组成最长的回文子序列。

加入s[j]的回文子序列长度为dp[i + 1][j]。

加入s[i]的回文子序列长度为dp[i][j - 1]。

那么dp[i][j]=max(dp[i+1][j],dp[i][j-1])

3.初始化

当i和j相同时,子序列就是一个字符,此时dp[i][j]一定是等于1的,即:一个字符的回文子序列长度就是1,其他情况dp[i][j]初始化为0就行,这样递推公式:dp[i][j]=max(dp[i+1][j],dp[i][j-1]);中dp[i][j]才不会被初始值覆盖

4.遍历顺序

从递归公式中,可以看出,dp[i][j] 依赖于 dp[i + 1][j - 1] ,dp[i + 1][j] 和 dp[i][j - 1],如图

5.推导dp

注意这里我们的结果返回位置在右上角

代码实现

class Solution {public int longestPalindromeSubseq(String s) {int m=s.length();//dp含义:dp[i][j]表示区间范围[i][j]子序列的长度int[][] dp=new int[m][m];//初始化,当i==j的时候,表示的子序列就是一个字符,那么肯定是回文的且长度为1for(int i=0;i<m;i++){dp[i][i]=1;}for(int i=m-1;i>=0;i--){for(int j=i+1;j<m;j++){if(s.charAt(i)==s.charAt(j)){dp[i][j]=dp[i+1][j-1]+2;}else{//说明不能同时添加s[i]和s[j],只能添加dp[i][j]=Math.max(dp[i+1][j],dp[i][j-1]);}}}return dp[0][m-1];//注意遍历的顺序和递推方向,最终结果回在第一排最后一个}
}


http://www.ppmy.cn/news/1123025.html

相关文章

找单身狗。一个数组中只有两个数字出现一次,其他数字出现了两次,编写一个函数找出这两个只出现一次的数字

例&#xff1a;在{1 2 3 4 5 6 1 2 3 4}找出5和6 方法二&#xff1a; 设计思想&#xff1a; 1.分组原理 &#xff08;1&#xff09;将所有数字进行异或&#xff0c;相同数字异或为零&#xff0c;所以只会剩5^6&#xff0c;即为异或的结果xor_result &#xff08;…

TLS/SSL(九) TLS1.2与TLS1.3中的ECDH协议

一 TLS1.2 与 TLS1.3 中的 ECDH 协议 TLS 1.3相比于TLS 1.2在性能和安全性有了很大的提升备注&#xff1a; 当前TLS1.2是主流,暂时关注1.2即可 国密TLS tls1.3 ① TLS1.2 通讯过程 说明&#xff1a; 需要wiresahrk分析报文加以赋证 ② FREAK攻击 客户端支持的很多安…

Docker 部署 Firefly III 服务

拉取最新版本的 Firefly III 镜像&#xff1a; $ sudo docker pull fireflyiii/core:latest在本地预先创建好 upload 和 export 目录, 用于映射 Firefly III 容器内的 /var/www/html/storage/upload 和 /var/www/html/storage/export 目录。 使用以下命令来运行 Firefly III …

go语言 rune 类型

ASCII 码只需要 7 bit 就能完整地表示&#xff0c;但只能表示英文字母在内的 128 个字符&#xff0c;为了表示世界上大部分的文字系统&#xff0c;发明了 Unicode &#xff0c;它是 ASCII 的超集&#xff0c;包含世界上书写系统中存在的所有字符&#xff0c;并且为每个代码分配…

点云从入门到精通技术详解100篇-机载 LiDAR 点云滤波及分类(中)

目录 3.2 研究区概况 3.3 两种滤波算法结果对比 3.4 结果评价 3.4.1 结果精度 3.4.2 结果对比

Linux学习笔记-应用层篇

1、Linux进程、线程概念/区别 Linux进程和线程是计算机系统中两种不同的资源分配和调度单位。 进程是计算机系统进行资源分配和调度的基本单位&#xff0c;也被认为是正在运行的程序。在面向线程的计算机结构中&#xff0c;进程是线程的容器。进程拥有独立的内存和系统资源&am…

openjdk和oracle jdk的区别

OpenJDK 和 Oracle JDK 都是 Java Development Kit (JDK) 的不同实现&#xff0c;用于开发和运行 Java 应用程序。它们有一些区别&#xff0c;但也有很多相似之处。以下是它们之间的主要区别&#xff1a; 开源性质&#xff1a; OpenJDK 是开源的&#xff0c;由一个社区维护和开…

Python_ithheima_第二阶段

第一章 01-初识对像 02 类的成员方法 03 类和对象 04 构造方法 05 魔术方法 06 封装 07 封装的课后练习题讲解 08 继承的基础语法 pass关键字的功能是“语法补全” 同名成员或方法&#xff0c;谁先来谁优先级高 09 复写父类成员和调用父类成员 10 变量的类型注解 11 函数和方法…