第一次写博客,有写的不好的地方希望大家多多给意见;以前都是用思维导图记笔记的,现在打算开始写写博客,分享自己学习中的一些东西。
本次内容分为以下几部分
背景
就以一道题目入场吧——最长回文子串,题目可参考:最长回文子串_牛客题霸_牛客网
再把描述和示例截图放上:
思路
暴力算法
本人脑子愚笨,一上来只想到了暴力解决:找到所有字串,判断是否为回文子串;如果是则更新结果。用到两层循环,时间复杂度为O(),空间复杂度为O(1),确实满足基础要求了,但是离进阶要求还差远呢。
附上一个表格形式的流程图...
当前元素 | a | b | a | b | c |
需要判断的字串 | a | b | a | b | c |
ab | ba | ab | bc | ||
aba | bab | abc | |||
abab | babc | ||||
ababc |
其实填充红色的那一行是没必要判断的,因为当输入非空时,最少都有一个回文子串,就是字符本身。因此需要判断的次数为
所以时间复杂度为O()
再附上python代码
class Solution:
def getLongestPalindrome(self , A: str) -> int:
# write code here
result = 1
for i in range(len(A)):
for j in range(len(A)):
if A[i:j+1] == A[i:j+1][::-1]:
result = max(result,j+1-i)
return result
中心扩散
接下来进入今天的主题——中心扩散算法
仔细思考我们会发现,回文字符串就是左右两边对称的字符串,如"aba","abba",根据这个思想,这时候如果我们以b为中心,向两边扩散,就能得到aba;同理对于abba,我们以bb为中心,向两边扩散,就能得到abba;
接下来我们整理出算法流程:
- 遍历所有元素
- 以当前元素为中心,向两边扩散,判断子串是否为回文,是则更新结果
- 以当前元素和右边一个元素为中心,向两边扩散,判断子串是否为回文,是则更新结果
接下来附上一个表格形式的流程图...
输入"ababc" | 输出应该是3,即子串"aba"或"bab" | |||||
当前元素 | a | b | a | b | c | |
应检查元素 | 以当前元素为中心 | a | b | a | b | c |
aba | bab | abc | ||||
ababc | ||||||
以当前元素和其右边元素为中心 | ba | ab | bc | |||
abab | babc |
同上面的暴力算法一样,填充红色的那一行是没必要进行检查的,可以看到需要判断的次数少于暴力算法,上面暴力算法对于这个示例需要检查1+2+3+4=10次,而中心扩散只需要检查2+3+4=9次。嗯……好像也没有少多少次的样子……
举个例子来说明一下为什么要检查这些子串,我们以第二个'a'为例子说明为什么要检查'bab','ababc','ab','babc'
- 如果以当前元素'a'为中心
- 首先我们左右各扩散一个元素,就会得到'bab',因此需要检查'bab'
- 其次我们左右各扩散两个元素,就会得到'ababc',因此需要检查'ababc'
- 接下来元素已经不够了,所以不需要扩散了,这里以左右两边拥有的最少元素进行扩散即可
- 如果以当前元素和其右边元素'ab'为中心(这里选左边也行)
- 首先我们左右各扩散零个元素,也就是检查中心本身(因为这时中心有两个元素,有可能它就是最长回文子串,这里跟上面以一个元素为中心有一点区别)
- 其次我们左右各扩散一个元素,就会得到'babc',因此需要检查'babc'
- 接下来我们想扩散两个元素,很明显右边已经不够扩散了,因此就没必要检查了;所以当以'ab'为中心时,右边只有一个元素'c',左右有两个元素'ba',因此我们只需扩散两次即可min(length('c'),length('ba')+1)
再附上python代码
class Solution:
def getLongestPalindrome(self , A: str) -> int:
# write code here
res = 1
le = len(A)
if le < 2:return le
for i in range(le-1):
for j in range(1,min(i+1,le-i)): # 以当前元素为中心,从1个元素开始扩展
if A[i-j:i+j+1] == A[i-j:i+j+1][::-1]:
res = max(res,2*j+1)
for j in range(min(i+1,le-i-1)): # 以当前元素和右边元素为中心,从0个元素开始扩展
if A[i-j:i+j+2] == A[i-j:i+j+2][::-1]:
res = max(res, 2*j+2)
return res
结语
上面的暴力算法和中心扩散复杂度都达到了 O(),好像这题用动态规划时间复杂度也要 O(),然后看到有大佬用Manacher算法可以把时间复杂度降到O(n),有兴趣的同学可以自行搜索。
感谢大家!