python返回最长回文子串的四种算法

1.暴力枚举法O(n^3)

        暴力枚举法寻找最长回文子串是通过检查字符串中的所有可能子串,并验证它们是否是回文来实现的。这种方法的时间复杂度很高,为 O(n^3),因为需要枚举所有子串并对每个子串进行回文验证。

暴力枚举法实现步骤

  1. 枚举所有子串:使用两层循环来生成字符串的所有可能子串。
  2. 检查每个子串是否为回文:对于每个子串,使用一个辅助函数来验证它是否是回文。
  3. 记录最长回文子串:如果找到的回文子串比已记录的更长,更新最长回文子串的长度。
def is_palindrome(s):
    return s == s[::-1]

def longest_palindrome_brute(s):
    max_len = 0
    n = len(s)
    for start in range(n):
        for end in range(start + 1, n + 1):
            substring = s[start:end]
            if is_palindrome(substring) and len(substring) > max_len:
                max_len = len(substring)
    return max_len

# 示例
input_string = "babad"
print(longest_palindrome_brute(input_string))  # 输出最长回文子串的长度
  • is_palindrome 函数:这个辅助函数接收一个字符串,通过简单的字符串反转检查它是否是回文。
  • longest_palindrome_brute 函数
    • max_len 用于跟踪找到的最长回文子串的长度。
    • 外层循环 start 从 0 开始,到字符串长度 n
    • 内层循环 endstart + 1 开始,直到 n + 1,这样可以包含所有可能的子串。因为range是左开右闭的,range(n+1),生成的数列只能到n,正好符合要求
    • 对于每个子串,使用 is_palindrome 函数检查是否为回文,并且如果是,并且长度大于 max_len,则更新 max_len

性能和限制

        虽然这种暴力枚举法简单直观,但由于其高时间复杂度,它在处理较长字符串时会非常慢。通常,对于实际应用,更推荐使用中心扩展或动态规划等更高效的算法。暴力枚举法通常用于教学目的或在数据量非常小的情况下使用。

2. 中心扩展法O(n^2)

        使用中心扩展法来寻找字符串中的最长回文子串是一种效率更高的方法,相比于暴力枚举法,它的时间复杂度通常是 O(n^2),并且空间复杂度很低。下面提供一个Python代码实现,用于寻找并返回字符串中的最长回文子串的长度:

def longest_palindrome(s):
    if not s:
        return 0

    def expand_around_center(left, right):
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1
        # 注意,当循环结束时,left 和 right 指向的字符不属于回文,
        # 正确的回文长度是 right - left - 1
        return right - left - 1

    max_len = 0
    for i in range(len(s)):
        # 以 s[i] 为中心的奇数长度回文
        len1 = expand_around_center(i, i)
        # 以 s[i] 和 s[i+1] 之间为中心的偶数长度回文
        len2 = expand_around_center(i, i + 1)
        # 更新找到的最长回文长度
        max_len = max(max_len, len1, len2)

    return max_len

# 示例
input_string = "babad"
print(longest_palindrome(input_string))  # 输出最长回文子串的长度
    left -= 1
    right += 1

return right - left - 1

问:为什么是left和right分别-1和+1,而返回的是-1而不是-2

答:比如xabcbaq,abcba是回文,

-1情况:两个a的索引分别是1和5,下一次循环的索引指向的x和q,索引是0和6,right-left=6,但实际回文长度是5,所以要right-left-1,

+1情况:如果在索引a的时候就停止,5-1=4,回文长度应该是5,所以是right-left+1。但我们的判断条件是越界了之后,所以就要-1

2出现的情况:两次差值确实是2

3.动态规划O(n^2)

动态规划是解决回文子串问题的一种非常有效的方法,特别是在需要频繁检索或涉及到更复杂结构的情况下。动态规划的核心思想是将大问题分解成小问题,并存储已解决问题的结果以避免重复计算。对于找到最长的回文子串,动态规划方法考虑所有可能的子串,并通过建立一个表来判断每个子串是否为回文。

动态规划算法概述:

  1. 初始化:创建一个二维布尔数组 dp[i][j],其中 dp[i][j] 表示从索引 i 到索引 j 的子串是否是回文。初始化所有单字符子串 dp[i][i]True,因为单个字符总是回文。

  2. 填充表格:对于长度大于1的子串,检查和更新 dp 表。如果 s[i] 等于 s[j] 且子串 s[i+1...j-1] 是回文(即 dp[i+1][j-1]True),则 s[i...j] 也是回文。

  3. 更新最长回文长度和起始点:每次发现一个回文子串时,检查其长度是否是当前已知的最长回文长度,如果是,则更新最长回文长度和起始索引。

def longest_palindrome_dp(s):
    n = len(s)
    if n < 2:
        return s  # 如果字符串长度小于2,直接返回原字符串

    dp = [[False] * n for _ in range(n)]
    start, max_len = 0, 1

    # 所有单个字符的子串都是回文
    for i in range(n):
        dp[i][i] = True

    # 构建DP表并找到最长回文子串
    for length in range(2, n + 1):  # 子串长度
        for i in range(n - length + 1):
            j = i + length - 1
            if s[i] == s[j]:
                if length == 2 or dp[i + 1][j - 1]:
                    dp[i][j] = True
                    if length > max_len:
                        max_len = length
                        start = i

    return s[start:start + max_len]

# 示例
input_string = "babad"
print(longest_palindrome_dp(input_string))  # 输出最长回文子串

4.Manacher算法O(n)

Manacher算法是一种高效的算法,用于在O(n)时间内找到一个字符串中最长的回文子串。这个算法由Albert Manacher于1975年提出,显著优于暴力法(O(n^3))和动态规划方法的时间复杂度(O(n^2)),因此在需要处理长字符串时非常有用。

Manacher算法的核心思想

Manacher算法的核心思想是利用回文的对称性和已经找到的信息来避免重复计算,从而实现线性时间复杂度。具体来说,它将原始字符串转换为一个新的字符串,这个新字符串在每个字符之间插入一个特殊字符(例如#),并在字符串的开始和结束插入两个特殊字符(例如^$)。这个处理确保了所有的回文都是奇数长度,从而简化了算法的实现。

主要步骤

  1. 预处理:将原始字符串转化为一个新字符串,在每个字符之间插入#,并在开始和结束插入^$。例如,字符串 "abcba" 变成 ^#a#b#c#b#a#$

  2. 初始化

    • p[i] 表示以 t[i] 为中心的回文的半径长度(包括中心字符)。
    • centerright 分别记录当前回文的中心和右边界。
  3. 遍历新字符串

    • 对每个字符 t[i],尝试扩展回文。
    • 使用已知的回文信息来减少计算工作。如果 i 在已知的回文右边界 right 内部,则可以利用 p[2*center - i] 这个信息来推测当前 i 处的回文长度。
    • 尝试扩展回文,更新 p[i]
    • 如果扩展的回文超过了右边界,更新 centerright
  4. 提取结果p[i] 数组中的最大值即为最长回文的长度(注意结果需要调整为原始字符串的长度)。

def manacher(s):
    # 转换字符串,每个字符间添加特殊字符
    t = '#'.join(f"^{s}$")
    n = len(t)
    p = [0] * n
    center = right = 0
    max_len = 0
    
    for i in range(1, n-1):
        p[i] = (right > i) and min(right - i, p[2*center - i])
        # 尝试扩展回文,即使之前已经部分计算过
        while t[i + p[i] + 1] == t[i - p[i] - 1]:
            p[i] += 1
        # 如果扩展的回文超过了右边界,更新中心和右边界
        if i + p[i] > right:
            center, right = i, i + p[i]
        # 更新最长回文长度
        max_len = max(max_len, p[i])
    
    return max_len

input_string = "babad"
print(manacher(input_string))  # 输出: 3

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值