概念
回文字符串是指一个字符串从左到右与从右到左遍历得到的序列是相同的。例如“abcba“就是回文字符串,而"abcab"则不是回文字符串。
回文字符串给定一个字符串,求它最长的回文子串长度,例如输入字符串'35534321',它的最长回文子串是'3553',所以返回4。
方法
求回文字符串的方法
目录
分析与解答:
最容易想到的方法为遍历字符串所有可能的子串(暴力法),判断其是否为回文字符串看,然后找出最长的回文字串。但是当字符串很长的时候,这种方法的效率是非常低下的,因此这种方法不可取。
给定字符串"",假设p(i, j)=1表示“”是回文字符串;P(i, j)=0表示“”不是回文字符串;
如果:那么P(i, i+1)=1,否则P(i, i+1) =0.
如过:那么P(i+1, j+1)=p(i, j).
————————————————
版权声明:本文为CSDN博主「灬蜡笔灬」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_42013574/article/details/88825930
1. 暴力枚举法
从长到短遍历,找到所有子串,判断每一个子串是否是回文.即a==a[::-1]
参考:https://blog.csdn.net/wangweimic/article/details/93977486
# 从长到短,依次遍历判断所有的子字符串,st= st[::-1]
def get_reverstring(string):
substring_length = len(string)
while substring_length > 0:
for i in range(len(string) - substring_length + 1):
# 因为从长到短,需要遍历[0:17];[0:16]、[1:17]...,所以需要另一个循环在每次更新要检测的子字符串长度时,重置上面的range()函数.
# i从一开始的0到0,1到0,1,2,是在被减去的数值范围内循环遍历
# 另一方面,依次递减需要检测的子字符串的长度,直到子字符串为空,长度为0,这是一个典型的while循环呀。
# 最后需要测试,检验代码是否正常运行
temp = string[i: i + substring_length]
if temp == temp[::-1]:
return temp
substring_length -= 1
print get_reverstring("abcdcbadegtefetge")
2. 中心搜索法
选取一个中心点index,按偶数和奇数两种情况进行遍历寻找,并将获得的子字符串进行比较,返回最大子字符串。
参考:https://blog.csdn.net/weixin_41863544/article/details/110493234
class Solution(object):
def center(self, left, right, s):
step = []
# 如果是奇数字符串
if left == right:
k = 1
while left - k >= 0 and left + k < len(s):
if s[left - k] == s[right + k]:
step = s[left - k: right + k + 1]
k += 1
else:
break
# 如果是偶数字符串
if left + 1 == right:
k = 0
while left - k >= 0 and right + k < len(s):
if s[left - k] == s[right + k]:
step = s[left - k: right + k + 1]
k += 1
else:
break
return step
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
l = []
for i in range(0, len(s) - 1):
# 假设i为奇数字符串中心的情况,进行回文字符串判断
step1 = self.center(i, i, s)
# 假设i为偶数字符串中心的情况,进行回文字符串判断
step2 = self.center(i, i + 1, s)
if len(l) < max(len(step1), len(step2)):
if len(step1) > len(step2):
l = step1
else:
l = step2
if l == []:
return s[0]
return l
s = "cdeffeba"
o = Solution()
print(o.longestPalindrome(s))
# leetcode submit region end(Prohibit modification and deletion)
# 可以将自定义的python函数封装成一个类class Solution(object),通过创建对象o = class_name(),对象调用类内创建的函数获得运行结果o.longestPalindrome(s)
3. 中心扩展法
枚举实现的耗时是我们无法忍受的。那么有没有高效查找回文子串的方法呢?答案当然是肯定的,那就是中心扩展法,选择一个元素作为中心,然后向外发散的寻找以该元素为圆心的最大回文子串。但是又出现了新的问题,回文子串的长度即可能是基数,也可能好是偶数,对于长度为偶数的回文子串来说是不存在中心元素的。那有什么方法解决中心扩展法中的偶数奇数、有中心无中心问题呢?
那是否有一种办法能将奇偶长度的子串归为一类,统一使用中心扩展法呢?它就是manacher算法,在原字符串中插入特殊字符,例如插入#后原字符串变成'#3#5#5#3#4#3#2#1#'。这样无论原字符串长度是奇数还是偶数,扩展后的字符串长度都是奇数,所以只需要以index为中心向两边扩展判断即可。
现在我们对新字符串使用中心扩展发即可,中心扩展法得到的不含中心点的半径长度就是子串的长度。
参考:https://www.cnblogs.com/baiyb/p/8326216.html
- 中心扩展法~优化前
def get_max_substr(string):
str_list = [s for s in string]
center_string = '#' + '#'.join(str_list) + '#'
# 以需要判断的字符串中心为循环条件,每次循环操作对象:全部的数组 --> 双循环
max_range = 0
max_substr = ''
for index in range(0, len(center_string)):
temp_range = 0
# 以index为中心扩展的字符串半径,最长是index,e.g. index =0, max_radius = 0; index = 1, max_radius = 1
for r in range(1, index + 1):
if index - r >= 0 and index + r <= len(center_string) - 1:
print center_string[index - r], center_string[index + r]
if center_string[index - r] == center_string[index + r]:
temp_range = r
else:
break
else:
break
# substr, substr_length = get_str_length(center_string, index)
if temp_range > max_range:
max_range = temp_range
max_substr = center_string[index - max_range : index + max_range + 1].replace('#', '')
return max_range, max_substr
if __name__ == "__main__":
string = "ecaacd"
max_range, max_substr = get_max_substr(string)
print max_range, max_substr
- 中心扩展法优化后
功能已经实现了,经过测试也没有bug,但是我们静下心来想一想,目前的解法是否还有优化空间呢?根据目前的解法,如果某个index3中心的最长回文子串长度比max_length要小,我们不必更新max_length。换句话说,我们计算以index3为中心的最长回文字串长度是做了无用功。
==> 优化:在遍历一个新的元素时,我们要优先判断它有没有潜质能超越max_length,如果不能超过,就继续遍历下一个元素。
def get_max_substr(string):
str_list = [s for s in string]
center_string = '#' + '#'.join(str_list) + '#'
# 以需要判断的字符串中心为循环条件,每次循环操作对象:全部的数组 --> 双循环
max_range = 0
max_substr = ''
for index in range(0, len(center_string)):
temp_range = 0
# 以index为中心扩展的字符串半径,最长是index,e.g. index =0, max_radius = 0; index = 1, max_radius = 1
for r in range(max_range + 1, index + 1):
# 以index为中心,r为半径的子字符串长度是否在整体字符串范围内-->优化:我觉得直接判断index中心有没有超过max_range的潜质
if index - max(max_range, r) >= 0 and index + max(max_range, r) <= len(center_string) - 1:
# 取子字符串时,注意排序
str_l = center_string[index - 1:index - max_range - 1: -1]
str_r = center_string[index + 1: index + max_range + 1]
# print str_l, str_r
if str_l == str_r:
if center_string[index - r] == center_string[index + r]:
temp_range = r
else:
break
else:
break
else:
break
if temp_range > max_range:
max_range = temp_range
max_substr = center_string[index - max_range: index + max_range + 1].replace('#', '')
return max_range, max_substr
if __name__ == "__main__":
string = "ecaac"
max_range, max_substr = get_max_substr(string)
print max_range, max_substr
github地址
yuyongsheng1990:https://github.com/yuyongsheng1990/python_training/tree/master/training_001_Longest_Palindrome_String