当前位置: 首页 > java >正文

最长回文子串(动规 + 中心拓展)

目录

  • [BM73 最长回文子串](https://www.nowcoder.com/practice/b4525d1d84934cf280439aeecc36f4af?tpId=295&tags=&title=&difficulty=0&judgeStatus=0&rp=0&sourceUrl=/exam/oj?questionJobId=10&subTabName=online_coding_page)
    • 1. 动态规划
      • (1)状态表示:
      • (2)状态转移方程
      • (3)初始化
      • (4)填表顺序
      • (5)返回值
    • 2. 中心拓展算法

BM73 最长回文子串

1. 动态规划

能够将所有的子串是否是回文的信息,保存在dp表里面.
时间/空间复杂度都是: O(n*n)

(1)状态表示:

首先我们这样想,如何表示出所有的子串?
我们假设 i 为子串的起始位置, j 为子串的结束位置,那我们就可以暴力枚举出所有的子串.当枚举完一个子串后, j 就不用从头开始了, 因为这时的子串和之前是一样的, 枚举下一个子串时 j 直接从 i 的位置开始即可.

暴力枚举的过程用两层for循环即可完成, 即用一个二维的dp表. 在二维dp表中,用横坐标表示起始位置 i ,用纵坐标表示结束位置 j .

如下图分析可知, 我们只需要使用dp表的上三角(i<=j) 部分即可:
在这里插入图片描述
在dp表中填上 true 或 false 即可知道该子串是否是回文了.

dp[i][j]表示: s 字符串 [i, j] 区间的子串是否是回文串.

(2)状态转移方程

要是回文串,首尾字符必须相同吧, 所以根据 s[i] 和 s[j] 的关系进行推导:
(1) s[i] != s[j], dp[i][j] = false
(2) s[i] == s[j] 时又有三种情况:
a. i==j 只有一个字符,是回文, dp[i][j] = true
b. i+1 = j 是相邻的, 也是回文, dp[i][j] = true
c. i 和 j 之间有别的字符了,则要去 [i+1, j-1] 的区间找了,即 dp[i+1][j-1], 如果它是 true, 则 dp[i][j] 是true, 否则 dp[i][j] 是 false.

(3)初始化

对 dp表 进行初始化时为了在后续填表时避免越界, 此时有可能会越界的是 dp[i+1][j-1].
当 i 和 j 相等或是 i 和 j 相邻的时候, 再进行 i + 1 和 j - 1 后, j > i 了, 但是我们在前面已经分析过了,使用的是dp表的上三角(i<=j), 此时不符合条件了. 但是其实这里是不会越界的, 因为会越界的两种情况就是上述分析的 a,b 两种情况, 已经特判过了.
所以, 可以不用进行初始化.

(4)填表顺序

我们填 (i, j) 位置时要用到 (i+1, j-1) 位置, 所以填表顺序整体是从下往上填写每一行.

(5)返回值

在 dp表 中是 true 的位置就是回文串, 知道了回文串的起始位置和结束位置, 一定可以计算出最长回文串的长度.

代码实现如下:

class Solution 
{
public:int getLongestPalindrome(string A) {//动态规划(n*n / n*n)//能够将所有的子串是否是回文的信息,保存在dp表里//dp[i][j]表示: s 字符串 [i,  j] 的子串是否是回文串.int n = A.size();vector<vector<bool>> dp(n, vector<bool>(n));int ret = 0;for(int i = n-1; i >= 0; i--){for(int j = i; j < n; j++) //注意: j >= i {//第一种情况: A[i] != A[j], 初始化默认是false//把后面三种情况合并在一起if(A[i] == A[j])dp[i][j] = i + 1 < j ? dp[i+1][j-1] : true;if(dp[i][j])ret = max(ret, j - i + 1);}}return ret;}
};

2. 中心拓展算法

中心拓展就是使用两个变量表示子串的起始位置和结束位置, 同时进行向左向右扩展, 这个过程可以分为奇数次扩展和偶数次扩展, 在计算出两种扩展的最大值即可.
时间复杂度: O(n)
空间复杂度: O(1)
在这里插入图片描述

代码实现如下:

class Solution 
{
public:int getLongestPalindrome(string A) {int n = A.size();int len = 1;//中心拓展算法(n*n / 1)//从下标为1的地方开始遍历for(int i = 1; i < n; i++){// 奇数次扩int left = i - 1, right = i + 1;while(left >= 0 && right < n && A[left] == A[right]){left --;right++;}len = max(len, right - left - 1);// 偶数次扩left = i - 1, right = i;while(left >= 0 && right < n && A[left] == A[right]){left --;right++;}len = max(len, right - left - 1);}return len;
};
http://www.xdnf.cn/news/4054.html

相关文章:

  • 反转字符串2
  • 杰理-JL701-充电开机,芯片不进入休眠
  • Spring Boot 中 @Bean 注解详解:从入门到实践
  • 无人机 | 无人机设计概述
  • Springclound常用五大组件及其使用原理
  • 防止交叉验证中的数据泄露:提升模型在实际环境中的性能
  • 怎样获得真实带宽之宽带升级后
  • 014枚举之指针尺取——算法备赛
  • C++类与对象深度解析:从基础到应用
  • kotlin 01flow-StateFlow 完整教程
  • Python-numpy中ndarray对象创建,数据类型,基本属性
  • 【免费分享无广告】刷视频助手抖音快手小红书视频号自动脚本刷视频养号
  • 前端面试每日三题 - Day 25
  • Netty的内存池机制怎样设计的?
  • 专业化婴幼儿托育服务与管理实训室建设方案
  • Easy云盘总结篇-回收站
  • 组合两个表 --- MySQL [Leetcode 题目详解]
  • 备战全国信息素养大赛 图形化挑战赛——约数和
  • “公共类 XXX 应该在文件中出现”错误怎么查找解决
  • 项目管理学习-CSPM(1)
  • MCP与API集成的最佳实践:高效连接,智能驱动
  • 谈判模拟器 - Gemini 2.5 商业优化版
  • JGQ626Ⅲ数据采集旋风除尘与袋式除尘组合实验装置
  • 【漫话机器学习系列】241.典型丢弃概率(Typical Dropout Probabilities)
  • EF Core 中,AsEnumerable 和 AsQueryable 的区别
  • 算法题(139):牛可乐和魔法封印
  • BUUCTF——Mark loves cat
  • 在Window10 和 Ubuntu 24.04LTS 上 Ollama 在线或离线安装部署
  • 嵌入式操作系统
  • 剥开 MP4 的 千层 “数字洋葱”:从外到内拆解通用媒体容器的核心