题目

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

示例

示例 1:

输入: "babad"

输出: "bab"

注意: "aba" 也是一个有效答案。

示例 2:

输入: "cbbd"

输出: "bb"

解答

方案1:暴力求解

遍历每一个子串,构建回文串判定函数(is_palindromic_string),用于判定每个子串是否为回文串,随时更新当前最大回文子串(max_substr)及其长度(max_len)。

class Solution:

def longestPalindrome(self, s):

is_palindromic_string = lambda s: s == s[::-1] # 回文串判定函数

max_len, max_substr = 0, ''

for i in range(len(s)):

for j in range(i+1, len(s)+1):

cur_substr = s[i:j] # 取当前子串

print(cur_substr)

if is_palindromic_string(cur_substr):

cur_len = len(cur_substr) # 当前子串长度

if cur_len > max_len:

max_len = cur_len

max_substr = cur_substr # 当前最长子串

return max_substr

算法的时间复杂度为O(N^3),果然,时间超过限制。

方案2:动态规划

这里,我们采用以空间换时间的动态规划策略,网上很多案例晦涩难懂,我们这里稍作改造。设我们的输入字符串为s:

1.构造二维矩阵dp,维度为len(s)*len(s),dp[left][right]表示子串s[left:right+1]是否为回文串,left和right均为下标索引,范围在0~len(s)之间,且left

2.构造状态转移方程,考虑三种情况,

(1)left = right,当前子串只有一个字符,一定为回文串;

(2)right = left + 1,当前子串只有两个字符,判断这两个字符是否相等;

(3)right > left + 1,当前子串有多个字符,判断根据首位字符是否相等以及除去首位字符剩余子串是否为回文串判定当前子串。

状态转移方程为:

或者把前两种情况归类:

class Solution:

def longestPalindrome(self, s):

dp = [[False]*len(s) for _ in range(len(s))]

max_start, max_len = 0, 0 # 最长回文子串开始位置及长度

for right in range(len(s)): # 右指针先走

for left in range(right+1): # 左指针跟着右指针

if right - left < 2: # 前两种情况

dp[left][right] = (s[left] == s[right])

else: # 最后一种情况

dp[left][right] = (s[left] == s[right]) and dp[left+1][right-1]

# cur_substr = s[left:right+1] # 当前考察的子串

cur_len = right + 1 - left # 当前子串长度为 right + 1 - left

if dp[left][right] and max_len < cur_len:

max_start = left

max_len = cur_len

return s[max_start:max_start + max_len]

时间和空间复杂度O(N^2),通过了验证,但是这个程序还是比较慢的:

执行用时 : 8980 ms, 击败了7.80% 的用户

内存消耗 : 20.7 MB, 中击败了17.62% 的用户

方案3:中心扩展

回文串有两个特点:

1.当回文串的左侧和右侧同时增加相同的字符时,构成的字符串也是回文串;

2.只有一个字符的字符串本身也是回文串;

根据这个特点,我们从头到尾遍历字符串,并且每遍历到一个位置时,我们都对这个位置的字符进行左右扩展,如果左右两头的元素相同,又可以构成新的回文子串,循环执行下去,直到左右两端元素不相同了为止,在此过程中,记录下每次扩展得到的最长子串起始位置和长度。需要注意的是,根据回文子串元素个数,我们需要考虑两种情况,一种是奇数个,一种是偶数个,这里我们用左右扩展的起始下标进行区分。

class Solution:

def longestPalindrome(self, s):

def extend(start_idx, end_idx, max_start, max_len):

"""

扩展函数,用于得到向左向右同步扩展后的最长回文子串

:param start_idx: 向左扩展的起始位置

:param end_idx: 向右扩展的起始位置

:param max_start: 当前最长回文子串的左指针

:param max_len: 当前最大长度

:return: 当前最长回文子串的起始位置和长度

"""

while 0 <= start_idx <= end_idx < len(s): # 子串起止下标合法时

if s[start_idx] == s[end_idx]: # 如果新增的两端字符相等

cur_str = s[start_idx:end_idx] # 当前子串是回文串

cur_len = end_idx + 1 - start_idx # 当前子串长度

if max_len < cur_len:

max_len = cur_len

max_start = start_idx

start_idx -= 1 # 左指针向左移动一位

end_idx += 1 # 右指针向右移动一位

else:

break

return max_start, max_len

max_start, max_len = 0, 0 # 初始化最长回文子串开始位置及长度

for i in range(len(s)): # 进行一次遍历

left = right = i # 判断长度为奇数的回文子串开始和结束位置

# 从i位置向两边扩展,搜寻可以得到的最大回文子串

max_start, max_len = extend(left, right, max_start, max_len)

left, right = i, i+1 # 判断长度为偶数的回文子串开始和结束位置

# 从i位置向左扩展,从i+1位置向右扩展,搜寻可以得到的最大回文子串

max_start, max_len = extend(left, right, max_start, max_len)

return s[max_start:max_start + max_len]

网上广为流传的还有一个办法,遍历字符串中每一个字符,主要考虑两种情况:

向左和向右各扩展一个字符,查看构成的新子串是否为回文串;

只向右扩展一个字符,查看构成的新子串是否为会问串。

这种方法要更加快些。

class Solution(object):

def longestPalindrome(self, s):

"""

:type s: str

:rtype: str

"""

is_palindromic_string = lambda s: s == s[::-1]

str_length = len(s)

cur_length = 0

start_index = 0

for i in range(str_length):

cur_start_index = i - cur_length - 1 # 向左扩展一个字符

cur_substr = s[cur_start_index: cur_start_index+cur_length+2] # 向右扩展一个字符,获得子串

if cur_start_index >= 0 and is_palindromic_string(cur_substr): # 判断扩展后的子串是否回文

start_index = cur_start_index # 更新子串起始下标

cur_length += 2 # 更新当前最长子串长度

else:

cur_start_index = i - cur_length # 向左不扩展

cur_substr = s[cur_start_index: cur_start_index+cur_length+1] # 向右扩展一个字符,获得子串

if cur_start_index >= 0 and is_palindromic_string(cur_substr): # 判断扩展后的子串是否回文

start_index = cur_start_index # 更新子串起始下标

cur_length += 1 # 更新当前最长子串长度

return s[start_index: start_index + cur_length]

其实还有疑问,为什么当两边扩展后就无需单边扩展,以及为何起始下标要这样设置,懂的大佬请在评论中留言。。

执行用时 : 92 ms, 击败了96.52% 的用户

内存消耗 : 13 MB, 击败了94.22% 的用户

方案4:Manacher's Algorithm(马拉车算法)

我们可以看到上面需要考虑子串是长度为奇数还是偶数两种情况,如何可以统一起来?马拉车算法应运而生。我们在输入字符用“#”隔开,且首位也添加“#”号,如“cbbcd”变成“#c#b#b#c#d#”,这样就可以确保字符串的长度为奇数了。

class Solution:

def longestPalindrome(self, s):

def get_length(string, index):

# 循环求出index为中心的最长回文字串

start, length = index // 2, 0

r_ = len(string)

for i in range(1, index + 1):

if index + i < r_ and string[index - i] == string[index + i]:

start = (index - i) // 2

length += 1

else:

break

return start, length

s_list = [c for c in s]

new_s = '#' + '#'.join(s_list) + '#' # 形成新串

max_start = max_length = 0

length = len(new_s)

for index in range(0, length):

start, r_length = get_length(new_s, index)

if max_length < r_length:

max_start, max_length = start, r_length

return s[max_start: max_start+max_length]

如有疑问,欢迎评论区留言~

最大子串和 python_5. 最长回文子串(Python)相关推荐

  1. 怎么判断一个字符串的最长回文子串是否在头尾_【Leetcode每日打卡】最长回文串...

    干货预警:所有文章都会首发于我的公众号[甜姨的奇妙冒险],欢迎watch. 一.来历: 力扣从3月开始开启了每日一题打卡活动,于是跟风加入了打卡大军,这两天写评论.发题解,没想到反响还不错,收到了来自 ...

  2. leetcode 5 :Longest Palindromic Substring 找出最长回文子串

    题目: Given a string S, find the longest palindromic substring in S. You may assume that the maximum l ...

  3. python【力扣LeetCode算法题库】5- 最长回文子串

    5. 最长回文子串 给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 的最大长度为 1000. 示例 1: 输入: "babad" 输出: "bab&quo ...

  4. leetcode算法题--最长回文子串

    题目链接:https://leetcode-cn.com/problems/longest-palindromic-substring/ 相关题目:最长回文子序列 动态规划 dp[i][j]表示从i到 ...

  5. LeetCode:Longest Palindromic Substring 最长回文子串

    Given a string S, find the longest palindromic substring in S. You may assume that the maximum lengt ...

  6. Manacher 求最长回文子串算法

    Manacher算法,是由一个叫Manacher的人在1975年发明的,可以在$O(n)$的时间复杂度里求出一个字符串中的最长回文子串. 例如这两个回文串"level"." ...

  7. 【字符串】最长回文子串 ( 动态规划算法 ) ★

    文章目录 一.回文串.子串.子序列 二.最长回文子串 1.动态规划算法 2.动态规划算法代码示例 一.回文串.子串.子序列 " 回文串 ( Palindrome ) " 是 正反都 ...

  8. 【字符串】最长回文子串 ( 蛮力算法 )

    文章目录 一.回文串.子串.子序列 二.最长回文子串 1.蛮力算法 2.时间复杂度最优方案 一.回文串.子串.子序列 " 回文串 ( Palindrome ) " 是 正反都一样的 ...

  9. 中心扩散算法--最长回文子串

    这篇看一下中心扩散算法. 输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案. 示例 2:输入: " ...

最新文章

  1. DatePicker的使用(一)
  2. MyGeneration【ui-原】
  3. 农行笔试,直接寄了,经验总结
  4. 2016年 第07届 蓝桥杯 Java B组 决赛真题详解及小结
  5. SpringMVC拦截器-拦截器的作用
  6. 在java中使用SPI创建可扩展的应用程序
  7. 报复性充值?《和平精英》iOS版上线三天 收入近1亿元
  8. 20200410:路径总和 Ⅰ Ⅱ(leetcode112 /113)
  9. python和java选择哪个-python和Java选择哪一个?
  10. android手机deviceowner,Android之解决Gigaset手机不能设置DeviceOwner权限提示already provisioned问题...
  11. HackTheBox 如何使用
  12. 干货!ERP系统优化生产管理流程五大步骤
  13. 音视频开发进阶|第七讲:分辨率与帧率·上篇
  14. 《微信小程序——发送模板消息》详细步骤
  15. ARP攻击,使用kali arpspoof
  16. python名字的来历_你知道Python的由来吗
  17. php就业薪资排名_山东第三季度就业难度指数报告:重工行业就业难
  18. ApowerMirror投屏(手机投屏电脑、电脑投屏到手机)
  19. tomcat(一个牛人写的文章,自己看)
  20. 【运维面试】面试官:你们的阿里云是怎么维护的?

热门文章

  1. Vagrant搭建开发环境1--总体介绍
  2. laravel的auth用户认证的例子
  3. Java读写二进制文件示例
  4. 选项卡jacascript
  5. php抓取dom处理后数据,PHP简单DOMDocument抓取排除td类
  6. php 创建自定义文件夹,Laravel 自定命令以及生成文件的例子
  7. 序列每天从0开始_序列化、反序列化原理和Protobuf实现机制
  8. 什么决定了计算机的寻址能力_有问有答:寻址能力与CPU的位宽有关系吗?
  9. CSS基础——CSS 三大特性【学习笔记】
  10. 黄金三月,技术自检 | 作为测试人必备的10项 Linux 技能