Python实现最长回文子串:从中心扩展到Manacher算法优化

沿途有光
沿途有光
发布时间:2026-10-13 | 3人浏览过
原创

本文详解Python求解最长回文子串长度的两种核心方法。首先介绍基础的中心扩展法,通过插入特殊字符统一处理奇偶长度回文串。随后深入分析Manacher算法的优化思路,利用已知最大半径提前剪枝,避免无效计算。通过对比1000个字符的测试数据,展示优化后算法性能提升约10倍的实际效果,并提供完整代码实现与测试案例。

给定一个字符串,目标是计算其包含的最长回文子串的长度。例如,对于输入字符串'35534321',其内部存在最长回文子串'3553',因此应返回数值4。

最直观的思路是枚举所有可能的子串,并逐一验证其是否为回文串,最终返回长度最大的那个。然而,这种暴力枚举的时间复杂度极高,在实际应用中往往难以接受。为此,我们可以采用中心扩展法来高效查找。该方法选取一个字符作为中心,向两侧扩散寻找以该点为圆心的最大回文子串。但这里存在一个难点:回文子串的长度可能是奇数也可能是偶数。对于偶数长度的回文串,并不存在单一的“中心字符”。为了解决这个问题,Manacher算法提出了一种预处理策略:在原字符串中插入特殊分隔符(如'#')。例如,原字符串'35534321'转换为'#3#5#5#3#4#3#2#1#'。经过这种变换后,无论原回文串长度奇偶,新字符串中均存在明确的中心点。此时使用中心扩展法得到的半径值,直接对应原回文子串的长度。

明确了上述思路后,具体实现步骤如下:首先将原始字符串'35534321'转换为'#3#5#5#3#4#3#2#1#'的形式,然后遍历新字符串中的每个元素,计算以该元素为中心的最长回文子串长度。以下是Python语言的具体实现代码:

#!/usr/bin/python
# -*- coding: utf-8 -*-

def max_substr(string):
  s_list = [s for s in string]
  string = '#' + '#'.join(s_list) + '#'
  max_length = 0
  length = len(string)
  for index in range(0, length):
    r_length = get_length(string, index)
    if max_length < r_length:
      max_length = r_length
  return max_length

def get_length(string, index):
  # 循环求出index为中心的最长回文字串
  length = 0
  r_ = len(string)
  for i in range(1,index+1):
    if index+i < r_ and string[index-i] == string[index+i]:
      length += 1
    else:
      break
  return length

if __name__ == "__main__":
  result = max_substr("35534321")
  print result

功能验证显示代码运行无误,但进一步思考可以发现优化空间。在当前解法中,我们计算了'35534321'中每个位置的中心最大回文长度。例如,当遍历到字符'4'时,已知当前记录的最长回文子串长度max_length为4。此时计算以'4'为中心的回文长度仅为3,小于max_length,因此无需更新max_length。这意味着之前的计算属于冗余操作。优化的核心在于:如果某个元素为中心扩展出的回文长度无法超越当前已知的max_length,则无需进行完整计算。在遍历新元素时,优先判断其潜在的最大回文范围是否可能超过max_length,若不可能,则直接跳过。以下是优化后的代码实现:

#!/usr/bin/python
# -*- coding: utf-8 -*-

def max_substr(string):
  s_list = [s for s in string]
  string = '#' + '#'.join(s_list) + '#'
  max_length = 0
  length = len(string)
  for index in range(0, length):
    r_length = get_length2(string, index, max_length)
    if max_length < r_length:
      max_length = r_length
  return max_length

def get_length2(string, index, max_length):
  # 基于已知的最长字串求最长字串
  # 1.中心+最大半径超出字符串范围, return
  r_ = len(string)
  if index + max_length > r_:
    return max_length

  # 2.无法超越最大半径, return
  l_string = string[index - max_length + 1 : index + 1]
  r_string = string[index : index + max_length]
  if l_string != r_string[::-1]:
    return max_length

  # 3.计算新的最大半径
  result = max_length
  for i in range(max_length, r_):
    if index-i >= 0 and index+i < r_ and string[index-i] == string[index+i]:
      result += 1
    else:
      break
  return result - 1

if __name__ == "__main__":
  result = max_substr("35534321")
  print result

性能提升效果如何?以包含1000个字符'1'的字符串为例进行基准测试:优化前的算法执行时间为0.239018201828秒,而优化后的算法执行时间缩短至0.0180191993713秒。由此可见,优化策略使执行速度提升了约10倍。

/usr/bin/python /Users/hakuippei/PycharmProjects/untitled/the_method_of_programming.py
0.239018201828
0.0180191993713

此外,分享另一个完整的测试实例供参考:

#!usr/bin/env python
#encoding:utf-8

'''
__Author__:沂水寒城
功能:寻找最长回文子序列
'''

def slice_window(one_str,w=1):
  '''
  滑窗函数
  '''
  res_list=[]
  for i in range(0,len(one_str)-w+1):
    res_list.append(one_str[i:i+w])
  return res_list


def is_huiwen(one_str_list): 
  '''
  输入一个字符串列表,判断是否为回文序列 
  ''' 
  if len(one_str_list)==1: 
    return True  
  else: 
    half=len(one_str_list)/2 
    if len(one_str_list)%2==0: 
      first_list=one_str_list[:half] 
      second_list=one_str_list[half:] 
    else: 
      first_list=one_str_list[:half] 
      second_list=one_str_list[half+1:] 
    if first_list==second_list[::-1]: 
      return True  
    else: 
      return False 


def find_longest_sub_palindrome_str(one_str):
  '''
  主函数,寻找最长回文子序列
  '''
  all_sub=[]
  for i in range(1,len(one_str)):
    all_sub+=slice_window(one_str,i)
  all_sub.append(one_str)
  new_list=[]
  for one in all_sub:
    if is_huiwen(list(one)):
      new_list.append(one)
  new_list.sort(lambda x,y:cmp(len(x),len(y)),reverse=True)
  print new_list[0]


if __name__ == '__main__':
  one_str_list=['uabcdcbaop','abcba','dmfdkgbbfdlg','mnfkabcbadk']
  for one_str in one_str_list:
    find_longest_sub_palindrome_str(one_str)

该实例的运行结果如下:

abcdcba 
abcba 
bb 
abcba 
[Finished in 0.3s] 

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表