LeetCode题目(Python实现):最长回文子串

题目

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例1 :

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。

示例2 :

输入: "cbbd"
输出: "bb"

想法一:以每个字符为中心向两边扫描

算法实现

def longestPalindrome(self, s: str) -> str:
    if not s:
        return ""
    res = s[0]
    n = len(s)
    for i in range(1, n):
        # 中轴情况
        j = 1
        while i - j > -1 and i + j < n and s[i - j] == s[i + j]:
            j += 1
        j -= 1
        if len(res) < j * 2 + 1:
            res = s[i - j:i + j + 1]

        # 对称情况
        j = 1
        while i - j > -1 and i + j - 1 < n and s[i - j] == s[i + j - 1]:
            j += 1
        j -= 1
        if len(res) < j * 2:
            res = s[i - j:i + j]
    return res

执行结果

执行结果 : 通过
执行用时 : 1176 ms, 在所有 Python3 提交中击败了68.60%的用户
内存消耗 : 13.6 MB, 在所有 Python3 提交中击败了33.03%的用户
在这里插入图片描述

复杂度分析

  • 时间复杂度:O(n^2)

  • 空间复杂度:O(1)

动态规划

算法实现

def longestPalindrome(self, s: str) -> str:
    size = len(s)
    if size < 2:
        return s

    dp = [[False for _ in range(size)] for _ in range(size)]

    max_len = 1
    start = 0

    for i in range(size):
        dp[i][i] = True

    for j in range(1, size):
        for i in range(0, j):
            if s[i] == s[j]:
                if j - i < 3:
                    dp[i][j] = True
                else:
                    dp[i][j] = dp[i + 1][j - 1]
            else:
                dp[i][j] = False

            if dp[i][j]:
                cur_len = j - i + 1
                if cur_len > max_len:
                    max_len = cur_len
                    start = i
    return s[start:start + max_len]

执行结果

在这里插入图片描述

复杂度分析

  • 时间复杂度:O(n^2)

  • 空间复杂度:O(n^2)

Manacher 算法

算法实现

def longestPalindrome(self, s: str) -> str:
    # 特判
    size = len(s)
    if size < 2:
        return s

    # 得到预处理字符串
    t = "#"
    for i in range(size):
        t += s[i]
        t += "#"
    # 新字符串的长度
    t_len = 2 * size + 1

    # 数组 p 记录了扫描过的回文子串的信息
    p = [0 for _ in range(t_len)]

    # 双指针,它们是一一对应的,须同时更新
    max_right = 0
    center = 0

    # 当前遍历的中心最大扩散步数,其值等于原始字符串的最长回文子串的长度
    max_len = 1
    # 原始字符串的最长回文子串的起始位置,与 max_len 必须同时更新
    start = 1

    for i in range(t_len):
        if i < max_right:
            mirror = 2 * center - i
            # 这一行代码是 Manacher 算法的关键所在,要结合图形来理解
            p[i] = min(max_right - i, p[mirror])

        # 下一次尝试扩散的左右起点,能扩散的步数直接加到 p[i] 中
        left = i - (1 + p[i])
        right = i + (1 + p[i])

        # left >= 0 and right < t_len 保证不越界
        # t[left] == t[right] 表示可以扩散 1 次
        while left >= 0 and right < t_len and t[left] == t[right]:
            p[i] += 1
            left -= 1
            right += 1

        # 根据 max_right 的定义,它是遍历过的 i 的 i + p[i] 的最大者
        # 如果 max_right 的值越大,进入上面 i < max_right 的判断的可能性就越大,这样就可以重复利用之前判断过的回文信息了
        if i + p[i] > max_right:
            # max_right 和 center 需要同时更新
            max_right = i + p[i]
            center = i

        if p[i] > max_len:
            # 记录最长回文子串的长度和相应它在原始字符串中的起点
            max_len = p[i]
            start = (i - max_len) // 2
    return s[start: start + max_len]

执行结果

在这里插入图片描述

复杂度分析

  • 时间复杂度:O(n)

  • 空间复杂度:O(n^2)

小结

先按自己的想法设计,也是之前做过的题,想了很久决定每个字符向两边扫描会好一点,然后做了出来。之后看了动态规划和 Manacher 算法,基本上理解了。

Manacher 算法说白了就是利用中心扩散的信息来省去一些计算,非常厉害,然后题解里面讲的很详细,虽然看了很久才明白,但是看完以后还是觉得大致理解了,很不错。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,有三种方法可以解决LeetCode上的最文子串问题。 方法一是使用扩展中心法优化,即从左向右遍历字符串,找到连续相同字符组成的子串作为扩展中心,然后从该中心向左右扩展,找到最文子串。这个方法的时间复杂度为O(n²)。\[1\] 方法二是直接循环字符串,判断子串是否是文子串,然后得到最文子串。这个方法的时间复杂度为O(n³),效率较低。\[2\] 方法三是双层for循环遍历所有子串可能,然后再对比是否反向和正向是一样的。这个方法的时间复杂度也为O(n³),效率较低。\[3\] 综上所述,方法一是解决LeetCode文子串问题的最优解法。 #### 引用[.reference_title] - *1* [LeetCode_5_最文子串](https://blog.csdn.net/qq_38975553/article/details/109222153)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Leetcode-最文子串](https://blog.csdn.net/duffon_ze/article/details/86691293)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [LeetCode 第5题:最文子串Python3解法)](https://blog.csdn.net/weixin_43490422/article/details/126479629)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值