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

这篇具有很好参考价值的文章主要介绍了【经典算法】LeetCode 5: 最长回文子串(Java/C/Python3实现含注释说明,Medium)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

题目描述

给定一个字符串 `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)出发最长回文串为多少。怎么寻找?
首先往左寻找与当期位置相同的字符,直到遇到不相等为止。
然后往右寻找与当期位置相同的字符,直到遇到不相等为止。
最后左右双向扩散,直到左和右不相等。如下图所示:
【经典算法】LeetCode 5: 最长回文子串(Java/C/Python3实现含注释说明,Medium),应聘,# 面试,数据结构,最长回文子串,字符串,算法,面试
每个位置向两边扩散都会出现一个窗口大小(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)

相似题目

(表格形式,列举出)文章来源地址https://www.toymoban.com/news/detail-851509.html

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

到了这里,关于【经典算法】LeetCode 5: 最长回文子串(Java/C/Python3实现含注释说明,Medium)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【LeetCode刷题】最长回文子串

    📝个人主页:爱吃炫迈 💌系列专栏:数据结构与算法 🧑‍💻座右铭:道阻且长,行则将至💗 题目:最长回文子串 思路一:暴力 枚举每一个子串,找回文串,然后通过比较,找出最长的回文串。 会超时 学习更多的JavaScript字符串方法,例如上面代码中用到的 split() , joi

    2023年04月23日
    浏览(34)
  • LeetCode刷题 | 647. 回文子串、516. 最长回文子序列

    给你一个字符串  s  ,请你统计并返回这个字符串中  回文子串  的数目。 回文字符串  是正着读和倒过来读一样的字符串。 子字符串  是字符串中的由连续字符组成的一个序列。 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

    2024年02月12日
    浏览(34)
  • 【算法沉淀】最长回文子串

     🎉🎉欢迎光临🎉🎉 🏅我是苏泽,一位对技术充满热情的探索者和分享者。🚀🚀 🌟特别推荐给大家我的最新专栏 《数据结构与算法:初学者入门指南》📘📘 希望能和大家一起学习!共同进步! 这是苏泽的个人主页可以看到我其他的内容哦👇👇 努力的苏泽 http://su

    2024年03月12日
    浏览(33)
  • 算法刷题|647.回文子串、516.最长回文子序列

    题目:给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。 d

    2024年02月17日
    浏览(37)
  • 最长子串和回文子串相关的算法题解

    中等 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s = “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 示例 2: 输入: s = “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。 示

    2024年02月19日
    浏览(28)
  • 【算法Hot100系列】最长回文子串

    💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老 导航 檀越剑指大厂系列:全面总

    2024年02月04日
    浏览(30)
  • 【算法题】动态规划中级阶段之最长回文子串、括号生成、跳跃游戏

    动态规划(Dynamic Programming,简称 DP)是一种解决多阶段决策过程最优化问题的方法。它是一种将复杂问题分解成重叠子问题的策略,通过维护每个子问题的最优解来推导出问题的最优解。 动态规划的主要思想是利用已求解的子问题的最优解来推导出更大问题的最优解,从而

    2024年02月12日
    浏览(32)
  • 【经典算法】 leetcode88.合并排序的数组(Java/C/Python3实现含注释说明,Easy)

    作者主页: 🔗进朱者赤的博客 精选专栏:🔗经典算法 作者简介:阿里非典型程序员一枚 ,记录在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法( 公众号同名 ) ❤️觉得文章还不错的话欢迎大家点赞👍➕收藏⭐️➕评论,💬支持博主,记得点个大大的 关

    2024年04月22日
    浏览(27)
  • 647.回文子串 516.最长回文子序列

    力扣题目链接(opens new window) 给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。 示例 1: 输入:“abc” 输出:3 解释:三个回文子串: “a”, “b”, “c” 示例 2: 输入

    2024年01月19日
    浏览(35)
  • 最长回文子串&最长子串&第K大的数字&atoi

    解题思路:中心扩散法 中心扩散法 其实,我们知道,对于回文子串来说,是对称的。也就是说,从中心开始,往左扩散,往右扩散,一直去比较左右两边,如果一样,就再去往左扩散,往后扩散,直到结束,如果出现不相等的情况,那就说明不是回文子串。我们来举个例子

    2023年04月08日
    浏览(82)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包