【LeetCode练习题】5. 最长回文子串

写在前面

这个题目之前在高中的时候就曾经写过,现在到了研究生很多东西需要再重新捡起来的呢,在面试中也曾经两次遇到这样的题目,现在在今天2020年3月12日10:14:49统一整理一下,预计花费2个小时的时间。

题目描述

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

示例 1:

输入: “babad” 输出: “bab” 注意: “aba” 也是一个有效答案。 示例 2:

输入: “cbbd” 输出: “bb”

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-palindromic-substring
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方法一:最长公共子串

最大的回文子串,可以替换成最长公共子串,两个字符串最长的公共的部分的,但是存在反向副本时,策略失效,需要补充条件进行判定,如果反向子串索引和原始索引相同则更新。
在这里插入图片描述在这里插入图片描述

花了一个小时学习最长公共子串的算法,花了半个小时想出了序号的判别方法。时间复杂度O(n2) leetcode
不能AC最后一个样例超时。吃个饭继续搞~

    def longestPalindrome( s):
        string1=s
        string2=s[::-1]
        len1 = len(string1)
        len2 = len(string2)
        res = [[0 for i in range(len1+1)] for j in range(len2+1)]  
        #python 初始化二维数组 [len1+1],[len2+1]
        result =0
        index=0
        for i in range(1,len2+1):  #开始从1开始,到len2+1结束
            for j in range(1,len1+1):  #开始从1开始,到len2+1结束
                if string2[i-1] == string1[j-1]:
                    res[i][j] = res[i-1][j-1]+1
                    if (result<=res[i][j])& (len(string1)-i==j-res[i][j]):
                        index=j
                        result = res[i][j]        
        string=string1[index-result:index] 
   
        return string  # 输出
 print(longestPalindrome("adabxxcbada"))

方法二:暴力法

在这里插入图片描述

方法三:动态规划方法

动态规划的方法,关键在于寻找边界状态。这个也不能AC,会超时,关于数组边界的问题,让我体会了一把python的金穗。
在这里插入图片描述
在这里插入图片描述

def longestPalindrome(s):
        if(s == ""):  return s
        strlen = len(s)
        dp=[[False for i in range(len(s))] for j in range(len(s))]
        for i in range(len(s)): dp[i][i] = True
        for i in range(len(s)-1): dp[i][i+1] = (s[i] == s[i+1])
        left = 0
        right = 0
        maxnum = 1;
        for i in range(len(s)-2,-1,-1):  #下左标 ,从0 开始 一定要倒置,否则计算出来的有问题,从小到大推过来的。
            for j in range(1,len(s)):  #下右标
                # P(i,j)=(P(i+1,j−1) and Si==Sj)
                if(i != j ) & (j != i+ 1):
                    dp[i][j] = dp[i+1][j-1] & (s[i] == s[j]);
                if(dp[i][j]) & (maxnum < j - i + 1):
                    maxnum = j - i + 1;
                    left = i;
                    right = j;
                print(i,j,dp[i][j])
        return s[left:right+1];
print( longestPalindrome("ab"))

方法四:中心扩展法

解法五: Manacher’s Algorithm 马拉车算法

马拉车算法 Manacher‘s Algorithm 是用来查找一个字符串的最长回文子串的线性方法,由一个叫 Manacher 的人在 1975 年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性。

首先我们解决下奇数和偶数的问题,在每个字符间插入 “#”,并且为了使得扩展的过程中,到边界后自动结束,在两端分别插入 “^” 和 “$”,两个不可能在字符串中出现的字符,这样中心扩展的时候,判断两端字符是否相等的时候,如果到了边界就一定会不相等,从而出了循环。经过处理,字符串的长度永远都是奇数了。

在这里插入图片描述
首先我们用一个数组 P 保存从中心扩展的最大个数,而它刚好也是去掉 “#” 的原字符串的总长度。例如下图中下标是 6 的地方,可以看到 P[ 6 ] 等于 5,所以它是从左边扩展 5 个字符,相应的右边也是扩展 5 个字符,也就是 “#c#b#c#b#c#”。而去掉 # 恢复到原来的字符串,变成 “cbcbc”,它的长度刚好也就是 5。
在这里插入图片描述

求原字符串下标

用 P 的下标 i 减去 P [ i ],再除以 2,就是原字符串的开头下标了。

例如我们找到 P[ i ] 的最大值为 5,也就是回文串的最大长度是 5,对应的下标是 6,所以原字符串的开头下标是(6 - 5 )/ 2 = 0。所以我们只需要返回原字符串的第 0 到 第(5 - 1)位就可以了。

求每个 P [ i ]

接下来是算法的关键了,它充分利用了回文串的对称性。

我们用 C 表示回文串的中心,用 R 表示回文串的右边半径。所以 R = C + P[ i ]。C 和 R 所对应的回文串是当前循环中 R 最靠右的回文串。

让我们考虑求 P [ i ] 的时候,如下图。

用 i_mirror 表示当前需要求的第 i 个字符关于 C 对应的下标。
在这里插入图片描述

class Solution:
     def center_spread(self, s,center):
        size = len(s)
        i = center - 1
        j = center + 1
        step = 0
        while i >= 0 and j < size and s[i] == s[j]:
            i -= 1
            j += 1
            step += 1
        return step
     def longestPalindrome(self,s):
        # 特判 差一个空格,5个缩进的样子
        size = len(s)
        if size < 2:
            return s

        # 得到预处理字符串
        t = "#"
        for i in range(size):
            t += s[i]
            t += "#"
        # 新字符串的长度
        t_len = 2 * size + 1
        # 当前遍历的中心最大扩散步数,其值等于原始字符串的最长回文子串的长度
        max_len = 1
        # 原始字符串的最长回文子串的起始位置,与 max_len 必须同时更新
        start = 0

        for i in range(t_len):
            cur_len = self.center_spread(t, i)
            if cur_len > max_len:
                max_len = cur_len
                start = (i - max_len) // 2
        return s[start: start + max_len]
   

正常缩进是5个空格,定义类的时候self.就是方法的函数,定义的函数需要放置在使用的函数之前,这一点和matlab不一致,和Pascal比较一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

和你在一起^_^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值