LeetCode5最长回文子串

这两天被这个题弄得是有些崩溃了,因为之前试的两种方法都是超时了,所以弄得后面都有些不想弄了。还好有度娘,最后用的是从中间往两边扩展的方法得此解决,真的是如释重负啊!废话不说,讲正文贴代码。

题目如下:

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

示例 1:

输入: "babad"
输出: "bab"
注意: "aba"也是一个有效答案。

示例 2:

输入: "cbbd"
输出: "bb"

方法一:暴力法

有些人会忍不住提出一个快速的解决方案,不幸的是,这个解决方案有缺陷(但是可以很容易地纠正):

反转 SS,使之变成 S'S′。找到 SS 和 S'S′ 之间最长的公共子串,这也必然是最长的回文子串。

这似乎是可行的,让我们看看下面的一些例子。

例如,S = {“caba”}S=“caba” , S' = {“abac”}S′=“abac”:

SS 以及 S'S′ 之间的最长公共子串为 {“aba”}“aba”,恰恰是答案。

让我们尝试一下这个例子:S ={“abacdfgdcaba”}S=“abacdfgdcaba” , S' = {“abacdgfdcaba”}S′=“abacdgfdcaba”:

SS 以及 S'S′ 之间的最长公共子串为{“abacd”}“abacd”,显然,这不是回文。

这种方法确实最好理解的额,但效率确实是低。所以放在编辑器里面跑,都是直接报超出时间限制的错误的,这是最dan疼的

def longestPalindrome(self, s):
        s_inverse = s[::-1]
        max = 0
        maxStr = ""
        if len(s) < 2:
            return s
        for start in range(len(s)):
            for end in range(start+1, len(s)+1):
                if s.count(s_inverse[start:end]) > 0:
                    index = s.index(s_inverse[start:end])
                    start_inverse = len(s) - end
                    if (end - start > max) and (index == start_inverse):
                        max = end - start
                        maxStr = s_inverse[start:end]
        return maxStr

这种方法是巧妙地运用了python 字符串的一些内置函数执行的,代码量很少,但跑起来时间不短

大概就是报这样的提醒,就是说你的程序没问题,但是太low了,到后面它都不愿意测试了

    def longestPalindrome(self, s):
        max = 0
        maxStr = ""
        if len(s) < 2:
            return s
        for start in range(len(s)):
            for end in range(len(s)-1, start-1, -1):
                start_copy = start
                end_copy = end
                while s[start_copy] == s[end_copy] and start_copy < end and end_copy >         
                      start:
                    start_copy += 1
                    end_copy -= 1
                if start_copy == end and end_copy == start:
                    if end - start >= max:
                        max = end - start
                        maxStr = s[start: end+1]
        return maxStr

这个方法是借鉴了动态规划的思想,但是也是效率过低,报如上的错误,贴出来可供参考

方法二:动态规划

为了改进暴力法,我们首先观察如何避免在验证回文时进行不必要的重复计算。考虑{“ababa”}“ababa” 这个示例。如果我们已经知道 {“bab”}“bab” 是回文,那么很明显,{“ababa”}“ababa” 一定是回文,因为它的左首字母和右尾字母是相同的。

我们给出 P(i,j)P(i,j) 的定义如下:

P(i,j) = \begin{cases} \text{true,} &\quad\text{如果子串} S_i \dots S_j \text{是回文子串}\\ \text{false,} &\quad\text{其它情况} \end{cases}P(i,j)={true,false,​如果子串Si​…Sj​是回文子串其它情况​

因此,

P(i, j) = ( P(i+1, j-1) \text{ and } S_i == S_j )P(i,j)=(P(i+1,j−1) and Si​==Sj​)

基本示例如下:

P(i, i) = trueP(i,i)=true

P(i, i+1) = ( S_i == S_{i+1} )P(i,i+1)=(Si​==Si+1​)

这产生了一个直观的动态规划解法,我们首先初始化一字母和二字母的回文,然后找到所有三字母回文,并依此类推…

复杂度分析

  • 时间复杂度:O(n^2)O(n2), 这里给出我们的运行时间复杂度为 O(n^2)O(n2) 。

  • 空间复杂度:O(n^2)O(n2), 该方法使用 O(n^2)O(n2) 的空间来存储表。

代码如下:

    def longestPalindrome(self, s):
        dpArray = np.zeros([1000, 1000])
        maxIndex = -1
        maxStr = ""
        if len(s) < 2:
            return s
        dpArray[0][0] = 1
        for end in range(1, len(s)):
            dpArray[end][end] = 1
            for start in range(end+1):
                if end == start+1 and s[end] == s[start]:
                    dpArray[start][end] = 1
                if end > start + 1:
                    dpArray[start][end] = dpArray[start+1][end-1] and (s[end] == s[start])
                if end - start >= maxIndex and dpArray[start][end]:
                    maxIndex = end - start
                    maxStr = s[start:end+1]
                    print(maxStr)
        print(dpArray[0:len(s), 0:len(s)])
        return maxStr

看起来确实是牛逼了一些,但不知为何在我的电脑上跑,它还是说超出时间限制,当时真的是快把我给气炸了

方法三:中心扩展算法

事实上,只需使用恒定的空间,我们就可以在 O(n^2)O(n2) 的时间内解决这个问题。

我们观察到回文中心的两侧互为镜像。因此,回文可以从它的中心展开,并且只有 2n - 1个这样的中心。

你可能会问,为什么会是 2n - 1个,而不是 n 个中心?原因在于所含字母数为偶数的回文的中心可以处于两字母之间(例如{“abba”}“abba” 的中心在两个 {‘b’}‘b’ 之间)。

代码如下:

    def longestPalindrome(self, s):
        maxLen = 0
        maxStr1 = ""
        for start_current in range(len(s)):
            if len(s) - start_current <= int(maxLen/2):
                break
            start_head = start_current
            start_last = start_current + 1
            while start_head >= 0 and start_last < len(s) and s[start_head] == s[start_last]:
                start_head -= 1
                start_last += 1
            if start_last - start_head - 1 >= maxLen:
                maxLen = start_last - start_head - 1
                maxStr1 = s[start_head + 1: start_last]
            start_head = start_current
            start_last = start_current
            while start_last < len(s) and start_head >= 0 and s[start_head] == s[start_last]:
                start_head -= 1
                start_last += 1
            if start_last - start_head - 1 >= maxLen:
                maxLen = start_last - start_head - 1
                maxStr1 = s[start_head + 1: start_last]
        return maxStr1

到了这种方法总算是给跑出来了,而且它的速度也算是中等吧!真的是一把鼻涕一把泪啊,本来想再把速度给提高的,但是被之前的失败经历给打击了,容我缓一段时间再来刚,最后把它的执行时间给贴出来

猜你喜欢

转载自blog.csdn.net/weixin_36431280/article/details/83539969