【经典算法】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数组为false
    char* 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 = 0
        end = 0
        for 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【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;在查询时效率可…

吴恩达AndrewNg 关于Agent工作流的分享

主要观点 &#x1f393; 基于HumanEval的测试,使用智能体工作流确实能够显著提升大语言模型的表现,有时甚至超过下一代更强大的模型。&#x1f504; AI智能体设计包括四种模式&#xff1a;反思、工具使用、规划、多智能体协作。&#x1f3d7;️ 快速token生成对于提高AI智能体…

智慧公厕是智慧城市建设中不可或缺的一部分

智慧城市的数字化转型正在取得显著成效&#xff0c;各项基础设施的建设也在迅速发展&#xff0c;其中智慧公厕成为了智慧城市体系中不可或缺的一部分。作为社会生活中必要的设施&#xff0c;公共厕所的信息化、数字化、智慧化升级转型能够实现全区域公共厕所管理的横向打通和纵…

第12章 集合框架

一 集合框架概述 1.1 生活中的容器 1.2 数组的特点与弊端 一方面&#xff0c;面向对象语言对事物的体现都是以对象的形式&#xff0c;为了方便对多个对象的操作&#xff0c;就要对对象进行存储。另一方面&#xff0c;使用数组存储对象方面具有一些弊端&#xff0c;而Java 集合…

设计模式 -- 发布订阅模式

发布订阅模式&#xff1a; 订阅者把自己想订阅的事件注册到调度中心&#xff0c;当发布者发布该事件到调度中心&#xff0c;也就是该事件触发时&#xff0c;由调度者统一调度订阅者注册到调度中心的处理代码。 在javaScript 中我们一般使用事件模型来代替传统的发布订阅模式。 …

一文搞懂路由器2.4G和5G的区别,以及双频合一模式

我们知道&#xff0c;无线路由器是平时生活和工作中最常见不过的一个无线设备&#xff0c;通过它我们的手机、笔记本、智能电视、摄像头等&#xff0c;都可以接入互联网。 其实WiFi在1998年就开始使用了&#xff0c;当时仅仅是在欧美地区小范围使用&#xff0c;我们国家在2008年…

关于Ansible模块 ④

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 继《关于Ansible的模块 ①》、《关于Ansible的模块 ②》与《关于Ansible的模块 ③》之后&#xff0c;继续学习ansible常用模块之…

C++流程控制语句:嵌套循环案例分析【九九乘法表】

在C编程中&#xff0c;循环语句的嵌套是一种常见且强大的技术手段&#xff0c;它允许我们将多个循环结构相互嵌套&#xff0c;形成多维循环。不论是for循环、while循环还是do…while循环&#xff0c;均可以进行嵌套。 而在实践中&#xff0c;由于for循环具有明确的循环变量初…

[法规规划|数据概念]数据要素市场三月速递

“ 代表关注&#xff0c;市场活跃&#xff0c;发展迅速” 01—听听两会代表怎么说 在2024年的全国两会期间&#xff0c;数据要素作为新型的生产要素受到广泛关注&#xff0c;众多代表围绕数据要素市场化、立法、安全监管、人才培养及基础设施建设等方面&#xff0c;积极建言献策…

基于centos7安装docker+k8s+KubeSphere

实验环境&#xff1a;&#xff08;每个服务器推荐内存为8G&#xff09; 服务器 ip地址 主机名 centos7 192.168.80.1…

模型量化——NVIDIA——方案选择(PTQ、 partialPTQ、 QAT)

PTQ、 partialPTQ、 QAT 选择流程 PTQ、 partialPTQ、 QAT 咨询NVIDIA 官方后&#xff0c;他们的校正过程一致&#xff0c;支持的量化算子本质是一样的&#xff0c;那么如果你的算子不是如下几类&#xff0c;那么需要自己编写算子。参考TensorRT/tools/pytorch-quantization/py…

数据库入门-----SQL基础知识

目录 &#x1f4d6;前言&#xff1a; &#x1f4d1;SQL概述&&通用语法&#xff1a; &#x1f433;DDL&#xff1a; &#x1f43b;操作数据库&#xff1a; &#x1f41e;数据类型&#xff1a; &#x1f989;操作表&#xff1a; &#x1f9a6;DML: 语法规则&#x…

helm与k8基础

文章目录 一、helm二、K8S/K3S1.K8S基本组件1.1 资源对象1.2 核心组件1.3典型的创建 Pod 的流程1.4 Kubernetes 多组件之间的通信原理 三、容器运行时 Containerd1.查看当前k3s使用的容器运行时CRI2.K3S修改docker为运行环境3. Containerd 参考 一、helm Helm是Kubernetes的包…

吴恩达机器学习理论基础解读—线性模型(单一特征拟合)

吴恩达机器学习理论基础——线性模型 机器学习最常见的形式监督学习&#xff0c;无监督学习 线性回归模型概述 应用场景一&#xff1a;根据房屋大小预测房价 应用场景二&#xff1a;分类算法&#xff08;猫狗分类&#xff09; 核心概念&#xff1a;将训练模型的数据称为数…

使用C语言函数对数组进行操作

前言 在我们了解数组和函数之后&#xff0c;我们对数组和函数进行结合&#xff0c;之后完成一些操作吧 题目描述 杰克想将函数与数组结合进行一些操作&#xff0c;以下是他想要达到的效果&#xff0c;请你帮帮他吧&#xff01; 创建一个整型数组&#xff0c;完成对数组的操作 1…

Taro框架中的H5 模板基本搭建

1.H5 模板框架的搭建 一个h5 的基本框架的搭建 基础template 阿乐/H5 Taro 的基础模板

人民网至顶科技:《开启智能新时代:2024中国AI大模型产业发展报告发布》

​3月26日&#xff0c;人民网财经研究院与至顶科技联合发布《开启智能新时代&#xff1a;2024年中国AI大模型产业发展报告》。该报告针对AI大模型产业发展背景、产业发展现状、典型案例、挑战及未来趋势等方面进行了系统全面的梳理&#xff0c;为政府部门、行业从业者以及社会公…

推荐一款自动化测试神器---Katalon Studio

Katalon Studio介绍 Katalon Studio 是一款在网页应用、移动和网页服务方面功能强大的自动化测试解决方案。基于 Selenium 和 Appium框架&#xff0c;Katalon Studio集成了这些框架在软件自动化方面的优点。这个工具支持不同层次的测试技能集。非程序员也可以快速上手一个自动…

5分钟了解清楚【osgb】格式的倾斜摄影数据metadata.xml有几种规范

数据格式同样都是osgb&#xff0c;不同软件生产的&#xff0c;建模是参数不一样&#xff0c;还是有很大区别的。尤其在应用阶段。 本文从建模软件、数据组织结构、metadata.xml&#xff08;投影信息&#xff09;、应用几个方面进行了经验性总结。不论您是初步开始建模&#xf…

Windows Server 2008添加Web服务器(IIS)、WebDAV服务、网络负载均衡

一、Windows Server 2008添加Web服务器&#xff08;IIS&#xff09; &#xff08;1&#xff09;添加角色&#xff0c;搭建web服务器&#xff08;IIS&#xff09; &#xff08;2&#xff09;添加网站&#xff0c;关闭默认网页&#xff0c;添加默认文档 在客户端浏览器输入服务器…