【经典算法】LeetCode 5: 最长回文子串(Java/C/Python3实现含注释说明,Medium)

目录

  • 题目描述
  • 思路及实现
    • 方式一:动态规划法
      • 思路
      • 代码实现
        • Java版本
        • C语言版本
        • Python3版本
      • 复杂度分析
    • 方式二:中心扩展法
      • 思路
      • 代码实现
        • Java版本
        • C语言版本
        • Python3版本
      • 复杂度分析
  • 总结
  • 相似题目

  • 标签(题目类型):回文串、动态规划

题目描述

给定一个字符串 `s`,找到其中最长的回文子串。可以假设 `s` 的最大长度为 1000。示例1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。示例2:
输入: "cbbd"
输出: "bb"

原题:LeetCode 5

思路及实现

方式一:动态规划法

思路

Dynamic Programming(DP) 动态规划是一种将问题分解成子问题并分别计算的优化技术。对于回文子串,我们可以使用动态规划来解决。

对于一个子串而言,如果它是回文串,并且长度大于 2,那么将它首尾的两个字母去除之后,它仍然是个回文串。例如对于字符串 “ababa”,如果我们已经知道 “bab” 是回文串,那么 “ababa” 一定是回文串,这是因为它的首尾两个字母都是 “a”。

通过定义一个二维数组 dp[i][j],表示 s 的第 i 个字符到第 j 个字符组成的子串是否为回文字符串。当 i == j 时,表示一个字符,是回文字符串,当 i + 1 == j ,则优先考虑两个字符是否相等来将问题规模缩小,同时考虑前一个子串是否为回文字符串。对于 i + 1 < j 的情况,可以通过判断 s[i] 和 s[j] 是否相等,并判断定义的 dp[i+1][j-1] 是否为回文字符串。如果是回文字符串,则 dp[i][j] 也是回文字符串。

代码实现

Java版本
class Solution {public String longestPalindrome(String s) {int n = s.length(); // 计算字符串的长度boolean[][] dp = new boolean[n][n]; // 创建一个二维数组用于记录子串是否为回文串String ans = ""; // 初始化最长回文子串为空字符串// 遍历所有长度的子串for (int len = 1; len <= n; len++) {// 遍历子串的起始位置for (int i = 0; i + len - 1 < n; i++) {int j = i + len - 1; // 子串的结束位置if (len == 1) {dp[i][j] = true; // 单个字符必定是回文串} else if (len == 2) {dp[i][j] = (s.charAt(i) == s.charAt(j)); // 只有两个字符时判断是否相等} else {dp[i][j] = (s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1]); // 多于两个字符时判断首尾字符是否相等}if (dp[i][j] && len > ans.length()) { // 如果当前子串是回文串并且长度更长ans = s.substring(i, j + 1); // 更新结果为当前子串}}}return ans;}
}

说明:
longestPalindrome 方法用于寻找给定字符串中的最长回文子串。使用动态规划的思想,创建一个二维数组 dp,其中 dp[i][j] 表示从索引 i 到索引 j 的子串是否为回文串。
通过遍历所有长度的子串,以及遍历子串的起始位置,判断子串是否为回文串,并根据回文串的长度更新最长回文子串 ans。
当子串的长度为 1 时,即一个字符,必定是回文串。
当子串的长度为 2 时,判断首尾两个字符是否相等。
当子串的长度大于 2 时,判断首尾两个字符是否相等,并根据 dp[i+1][j-1] 的结果判断子串是否为回文串。
如果当前子串是回文串,并且长度比之前记录的最长回文子串长度更长,则更新最长回文子串 ans。
最后,返回最长回文子串。

C语言版本

char* longestPalindrome(char* s) {int n = strlen(s); // 计算字符串的长度bool dp[n][n]; // 创建一个二维数组用于记录子串是否为回文串memset(dp, false, sizeof(dp)); // 初始化dp数组为falsechar* ans = ""; // 初始化最长回文子串为空字符串// 遍历所有长度的子串for (int len = 1; len <= n; len++) {// 遍历子串的起始位置for (int i = 0; i + len - 1 < n; i++) {int j = i + len - 1; // 子串的结束位置if (len == 1) {dp[i][j] = true; // 单个字符必定是回文串} else if (len == 2) {dp[i][j] = (s[i] == s[j]); // 只有两个字符时判断是否相等} else {dp[i][j] = (s[i] == s[j] && dp[i + 1][j - 1]); // 多于两个字符时判断首尾字符是否相等}if (dp[i][j] && len > strlen(ans)) { // 如果当前子串是回文串并且长度更长char* sub = (char*)malloc((len + 1) * sizeof(char)); // 分配内存保存当前子串strncpy(sub, s + i, len); // 复制当前子串到内存中sub[len] = '\0'; // 添加字符串结束标志ans = sub; // 更新结果为当前子串}}}return ans;
}

说明: (同上)
longestPalindrome 函数用于寻找给定字符串中的最长回文子串。使用动态规划的思想,创建一个二维数组 dp,用于记录子串是否为回文串。
通过遍历所有长度的子串,以及遍历子串的起始位置,判断子串是否为回文串,并根据回文串的长度更新最长回文子串 ans。
子串的判断分三种情况:
当子串的长度为 1 时,即一个字符,必定是回文串。
当子串的长度为 2 时,判断首尾两个字符是否相等。
当子串的长度大于 2 时,判断首尾两个字符是否相等,并根据 dp[i+1][j-1] 的结果判断子串是否为回文串。
如果当前子串是回文串,并且长度比之前记录的最长回文子串长度更长,则更新最长回文子串 ans。
最后,返回最长回文子串。

Python3版本
class Solution:def longestPalindrome(self, s: str) -> str:n = len(s)dp = [[False] * n for _ in range(n)]ans = ""# 遍历所有长度的子串for length in range(1, n + 1):# 遍历子串的起始位置for i in range(n - length + 1):j = i + length - 1  # 子串的结束位置if length == 1:dp[i][j] = True  # 单个字符必定是回文串elif length == 2:dp[i][j] = (s[i] == s[j])  # 只有两个字符时判断是否相等else:dp[i][j] = (s[i] == s[j] and dp[i + 1][j - 1])  # 多于两个字符时判断首尾字符是否相等if dp[i][j] and length > len(ans):  # 如果当前子串是回文串并且长度更长ans = s[i:j + 1]  # 更新结果为当前子串return ans

说明: (同上)
longestPalindrome 方法用于寻找给定字符串中的最长回文子串。使用动态规划的思想,创建一个二维数组dp,其中dp[i][j]表示从索引i到索引j的子串是否为回文串。通过遍历所有长度的子串,从最短的子串起,依次判断是否为回文串,并根据判断结果更新最长回文子串。最后,返回最长回文子串。
dp[i][j]的判断依据如下:
当子串长度为1时,dp[i][j]为True,因为单个字符必定是回文串;
当子串长度为2时,子串为回文串的条件是s[i]和s[j]相等;
当子串长度大于2时,子串为回文串的条件是首尾字符相等且去除首尾字符的子串也为回文串。
当发现一个更长的回文子串时,将其更新为结果。

复杂度分析

  • 时间复杂度:该算法的时间复杂度为 O(n^2),其中 n 是字符串的长度。双重循环遍历了所有长度为 1 到 n 的子串。
  • 空间复杂度:该算法的空间复杂度为 O(n^2),存储了一个二维数组 dp。

方式二:中心扩展法

思路

中心扩展法的基本思路是从左至右遍历字符串,以当前字符和其相邻字符为中心,向两边扩展,判断是否为回文串。在判断时,可以将回文串的起始和结束位置保存下来。

从每一个位置出发,向两边扩散即可。遇到不是回文的时候结束。举个例子,str=acdbbdaa 我们需要寻找从第一个 b(位置为 3)出发最长回文串为多少。怎么寻找?
首先往左寻找与当期位置相同的字符,直到遇到不相等为止。
然后往右寻找与当期位置相同的字符,直到遇到不相等为止。
最后左右双向扩散,直到左和右不相等。如下图所示:
在这里插入图片描述
每个位置向两边扩散都会出现一个窗口大小(len)。如果 len>maxLen(用来表示最长回文串的长度)。则更新 maxLen 的值。
因为我们最后要返回的是具体子串,而不是长度,因此,还需要记录一下 maxLen 时的起始位置(maxStart),即此时还要 maxStart=len。

代码实现

Java版本
class Solution {public String longestPalindrome(String s) {int start = 0; // 回文串的起始位置int end = 0; // 回文串的结束位置for (int i = 0; i < s.length(); i++) {int len1 = expandAroundCenter(s, i, i); // 以当前字符为中心的回文串长度int len2 = expandAroundCenter(s, i, i + 1); // 以当前字符和下一个字符为中心的回文串长度int len = Math.max(len1, len2); // 取较长的回文串长度if (len > end - start) {start = i - (len - 1) / 2; // 更新回文串的起始位置end = i + len / 2; // 更新回文串的结束位置}}return s.substring(start, end + 1); // 根据起始位置和结束位置返回最长回文子串}private int expandAroundCenter(String s, int left, int right) {while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {left--; // 向左扩展right++; // 向右扩展}return right - left - 1; // 返回回文串的长度}
}

说明:
longestPalindrome 方法用于寻找给定字符串中的最长回文子串。根据中心扩展法的思想,遍历字符串中的每个字符,以当前字符为中心向两边扩展,同时以当前字符和下一个字符为中心向两边扩展,获取回文串的长度。通过比较回文串的长度,不断更新最长回文串的起始位置和结束位置。最后,根据起始位置和结束位置从原字符串中截取出最长回文子串并返回。
expandAroundCenter 方法用于以指定的左右位置为中心向两边扩展,判断是否为回文串。通过比较左右位置的字符是否相等,并同时向左和向右移动位置来不断扩展回文串的长度。最后,返回回文串的长度。

C语言版本
char* longestPalindrome(char* s) {int len = strlen(s);int start = 0;int end = 0;for (int i = 0; i < len; i++) {int len1 = expandAroundCenter(s, i, i);int len2 = expandAroundCenter(s, i, i + 1);int len = len1 > len2 ? len1 : len2;if (len > end - start) {start = i - (len - 1) / 2; // 更新回文串起始位置end = i + len / 2; // 更新回文串结束位置}}char *longest_palindrome = malloc((end - start + 2) * sizeof(char));strncpy(longest_palindrome, s + start, end - start + 1); // 复制回文串至新分配的内存longest_palindrome[end - start + 1] = '\0';return longest_palindrome;
}int expandAroundCenter(char *s, int left, int right) {while (left >= 0 && right < strlen(s) && s[left] == s[right]) {left--; // 向左扩展right++; // 向右扩展}return right - left - 1; // 返回回文串的长度
}

说明:
longestPalindrome 函数用于寻找给定字符串中的最长回文子串。通过遍历字符串并以每个字符为中心依次判断以该字符或相邻两个字符为中心的回文串长度。通过比较回文串的长度,不断更新最长回文串的起始位置和结束位置。最后,将最长回文串从原字符串复制到新分配的内存中并返回。
expandAroundCenter 函数用于在给定字符串中以指定的左右位置为中心向两边扩展,判断是否为回文串。通过比较左右位置的字符是否相等,并同时向左和向右移动位置来不断扩展回文串的长度。最后,返回回文串的长度。

Python3版本
class Solution:def longestPalindrome(self, s: str) -> str:start = 0end = 0for i in range(len(s)):len1 = self.expandAroundCenter(s, i, i)len2 = self.expandAroundCenter(s, i, i + 1)length = max(len1, len2)if length > end - start:start = i - (length - 1) // 2 # 更新回文串起始位置end = i + length // 2 # 更新回文串结束位置return s[start: end + 1] # 根据起始位置和结束位置返回最长回文子串def expandAroundCenter(self, s: str, left: int, right: int) -> int:while left >= 0 and right < len(s) and s[left] == s[right]:left -= 1 # 向左扩展right += 1 # 向右扩展return right - left - 1 # 返回回文串的长度
}

说明:
longestPalindrome 函数用于寻找给定字符串中的最长回文子串。通过遍历字符串并以每个字符为中心或相邻两个字符为中心依次判断以该中心为起点的回文串长度。通过比较回文串的长度,不断更新最长回文串的起始位置和结束位置。最后,根据起始位置和结束位置从原字符串中截取出最长回文子串并返回。
expandAroundCenter 函数用于在给定字符串中以指定的左右位置为中心向两边扩展,判断是否为回文串。通过比较左右位置的字符是否相等,并同时向左和向右移动位置来不断扩展回文串的长度。最后,返回回文串的长度。

复杂度分析

  • 时间复杂度:该算法的时间复杂度为 O(n^2),其中 n 是字符串的长度。在中心扩展法中,每个字符仅遍历一次。
  • 空间复杂度:该算法的空间复杂度为 O(1),只使用了常量级的额外空间。

总结

方式备注优点缺点时间复杂度空间复杂度
动态规划法通过动态规划计算回文子串算法稳定可靠需要额外的二维数组存储状态O(n^2)O(n^2)
中心扩展法通过扩展中心位置计算回文子串具有较高效率对空间的使用较低O(n^2)O(1)

相似题目

(表格形式,列举出)

相似题目难度链接
两个数组的交集简单leetcode-349

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

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

相关文章

Redis入门--头歌实验使用Redis构建自动补全组件

自动补全与输入联想功能已经是大多数网站的标配&#xff0c;给表单加入自动补全功能大大节省了用户输入时间&#xff0c;而输入联想功能则起到了预测用户喜好的作用&#xff0c;两个功能都是提升用户体验的利器。 本实训&#xff0c;我们通过实现搜索历史、自动补全和搜索预测三…

【C++】unordered 系列关联式容器

文章目录 1. unordered 系列关联式容器2. unordered_map2.1 unordered_map 的文档介绍2.2 unordered_map 的接口说明 3. unordered_set4. 在线 OJ 1. unordered 系列关联式容器 在 C 98 中&#xff0c;STL 提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可…

LeetCode-热题100:300. 最长递增子序列

题目描述 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 示…

mybatis(4) 代理机制以及小技巧

mybatis 1、 问题2、使用3、#与$3.1 例子3.2什么情况下必须使用${} 4、别名机制5 、Mapper配置6、配置模板文件7、使用自动生成的主键 1、 问题 在上一篇mybtis的web项目中&#xff0c;我们最后有一个问题没有解决&#xff0c;就是dao的实现类&#xff0c;其代码很类似&#x…

Python 正则表达式(re)

&#x1f680; 个人主页&#xff1a;xmp65535 &#x1f680; 专栏&#xff1a;python技术专栏 目录 一、概述 二、基本概念 模式字符串 原始字符串 编译正则表达式 三、正则表达式的基本模式匹配 字符匹配 锚点 量词 四、常用函数 re.match() re.search() re.find…

Mybase 8.x 下载安装试用详细教程(Windows)

一、软件介绍 MyBase是一款本地笔记软件&#xff0c;它能够帮助你管理和组织大量的信息。 二、下载安装 下载&#xff1a;点我去下载页面 Mybase 软件目前主要实现了两种版本&#xff1a;Mybase Desktop (桌面版) 和 Mybase Server (网络版)&#xff1a; 桌面版软件运行于 …

零售行业数字化广告评价标准 - 《IAB/MRC零售(广告)测量指南》

IAB/MRC零售&#xff08;广告&#xff09;测量指南 --- 最新标准&#xff0c;2024年1月发布 目录 1出台此标准的目的是什么&#xff1f;2标准宗旨3本标准的主要关键领域4为什么这对品牌和零售商很重要5能给零售媒体中小型玩家带来什么机会&#xff1f;6评价零售媒体效果的最…

金仓数据库Kingbase的数据库开发管理工具KStudio连接乱码

背景&#xff1a; 金仓数据库V8R6&#xff0c;KStudio在Windows10上运行&#xff0c;JDK8 问题&#xff1a; 使用客户端连接数据库时&#xff0c;提示信息乱码&#xff0c;首选项设置字符集不管用&#xff0c;具体如下图所示&#xff1a; Before&#xff1a; After&#xff1…

谷歌(Google)历年编程真题——给字符串添加加粗标签

谷歌历年面试真题——数组和字符串系列真题练习。 给字符串添加加粗标签 给定字符串 s 和字符串数组 words。 对于 s 内部的子字符串&#xff0c;若其存在于 words 数组中&#xff0c; 则通过添加闭合的粗体标签 <b> 和 </b> 进行加粗标记。 如果两个这样的子字…

软件设计师——1.备考提纲

知识点说明比例软件工程基础知识11开发模型、设计原则、测试方法、质量特性、CMM、Pert图、风险管理14.67%面向对象12面向对象基本概念、面向对象分析与设计、UML、设计模式16.00%数据结构与算法10数组、栈、队列、树与二叉树、图、查找与排序、常见算法13.33%程序设计语言6文法…

【CPA考试】2024注册会计师报名照片尺寸要求解读及手机拍照方法

随着2024年注册会计师考试的临近&#xff0c;众多会计专业人士和学生都开始准备报名参加这一行业的重要考试&#xff0c;报名时间为4月8日至4月30日。报名过程中&#xff0c;一张符合要求的证件照是必不可少的。本文将为您详细解读2024年注册会计师考试报名照片的尺寸要求&…

sfml sdl2 windows vscode 调试和coderunner插件运行

链接库写在编译链接命令里&#xff0c;如果没有使用到不会加入到生成的可执行文件里。所以tasks.json可以这样写&#xff0c; {"version": "2.0.0","tasks": [{"type": "cppbuild","label": "C/C: g.exe 生…

GlusterFS分布式文件系统

一、GlusterFS简介 GlusterFS 是一个开源的分布式文件系统。由存储服务器、客户端以及NFS/Samba存储网关(可选&#xff0c;根据需要选择使用)组成。没有元数据服务器组件&#xff0c;这有助于提升整个系统的性能、可靠性和稳定性 二、GlusterFS特点 2.1 扩展性和高性能 Glu…

结合 linux 0.11 源码分析为什么 fork 函数会返回两个值

#&#xff08;1&#xff09;学习资料是这些书 以及赵炯博士的完全剖析。谢谢这些大师与网上优秀的文章。 #&#xff08;2&#xff09;看 linux 0.11 源码是 sourceinlight 4.这里附上一个安装包。 由本图可知&#xff0c; main 函数对 fork 函数的调用。 fork 函数实际是定…

掌握苹果电脑保养秘籍,全方位维护指南!

苹果电脑&#xff08;Mac&#xff09;凭借卓越的性能表现、精致的设计美学以及深入人心的操作系统体验赢得了全球用户的高度评价。不过&#xff0c;无论是哪款顶级配置的Mac产品&#xff0c;都需要经过适时且恰当的保养维护&#xff0c;才能持续展现最优性能。 苹果电脑维护小…

python应用题例子--改试卷

要求&#xff1a;在现实生活中&#xff0c;写一些小函数帮助老师改卷子&#xff0c;比如选择题&#xff0c;本次数学题有12道&#xff0c;正解答案是ABCDCCDDBABA,请根据正确答案&#xff0c;找出学生的错误题并统计正常率。 CORRECT_ANSWERS"ABCDCCDDBABA" #一般习…

最新AI工具系统ChatGPT网站运营源码SparkAi系统V6.0版本,GPTs应用、AI绘画、AI换脸、垫图混图、Suno-v3-AI音乐生成大模型全支持

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧。已支持GPT…

Django之五种中间件定义类型—process_request、process_view、process_response.......

目录 1. 前言 2. 基础中间件 3. 如何自定义中间件 4. 五种自定义中间件类型 4.1 process_request 4.2 process_view 4.3 process_response 4.4 process_exception 4.5 process_template_response 5. 最后 1. 前言 哈喽&#xff0c;大家好&#xff0c;我是小K,今天咋们…

openhomeny鸿蒙arkts笔记

基于api9&#xff0c;创建arkts file&#xff0c;之前是java或者kotlin代码&#xff0c;转鸿蒙代码的一些笔记 1、final用readonly代替 2、Exception用Error代替 3、String用string代替 4、int&#xff0c;long&#xff0c;float&#xff0c;double等用number代替 5、要给…

探索GlassWire:网络安全与流量监控软件

名人说:东边日出西边雨,道是无晴却有晴。——刘禹锡 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、软件介绍1、GlassWire(免费版本)2、核心特点二、下载安装① 下载(免费版本)② 安装三、使用方法①如何使用?②语言切换③流量监控四、总结