leetcode刷题记录——5、Longest Palindromic Substring

题目

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

示例

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

按照惯例,先都使用暴力方法,找出所有回文子串比较长度。不过,提交超时,哈哈哈哈。

class Solution(object):
    def isPalindrome(self, substring):
        length = len(substring)
        for i in range(length // 2):
            if substring[i] != substring[length - i - 1]:
                return False
        return True
    
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        if s == "":
            return s
        longestPalindrome = s[0]
        for i in range(2, len(s) + 1):
            for j in range(len(s) + 1 - i):
                substring = s[j : j + i]
                if self.isPalindrome(substring):
                    longestPalindrome = substring
                    break
        return longestPalindrome

复杂度分析

时间复杂度O(n^{3}),第一层循环是回文长度,从2到n,第二层循环遍历字符串,虽然减少了部分遍历,但两层循环也达到n^{^{2}}级别,判断是否回文又是一个n级别,总体达n^{^{3}}级别。

空间复杂度O(n)

 

暴力方法改进,奇数长度的回文子串必然包含更小奇数长度的回文子串,偶数长度的回文子串也必然包含更小偶数长度的回文子串,比如长度为3的回文子串中包含长度为1的回文子串,长度为5的回文子串中包含长度为3的回文子串,若长度为3的回文子串找不到,则不需要再找,偶数长度同理。因此,可提前结束查找,大大减少计算量。改进后提交成功,执行用时500ms左右。

class Solution(object):
    def isPalindrome(self, substring):
        length = len(substring)
        for i in range(length // 2):
            if substring[i] != substring[length - i - 1]:
                return False
        return True
    
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        if s == "":
            return s
        longestPalindrome = s[0]
        even_found = True
        odd_found = True
        for i in range(2, len(s) + 1):
            if i % 2 == 0 and even_found:
                for j in range(len(s) + 1 - i):
                    substring = s[j : j + i]
                    if self.isPalindrome(substring):
                        even_found = True
                        longestPalindrome = substring
                        break
                    else:
                        even_found = False
            if i % 2 != 0 and odd_found:
                for j in range(len(s) + 1 - i):
                    substring = s[j : j + i]
                    if self.isPalindrome(substring):
                        odd_found = True
                        longestPalindrome = substring
                        break
                    else:
                        odd_found = False
        return longestPalindrome

复杂度分析

时间复杂度O(n^{3}),虽然达到提前结束查找的效果,但最坏情况下,依旧是n^{3}级别。

空间复杂度O(n)

 

再改进,去除判断回文函数,使用字符串反转判断,可达200ms,增加初始判断条件,可达100ms。

class Solution(object):
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        if len(s) < 2 or s == s[::-1]:
            return s
        longestPalindrome = s[0]
        even_found = True
        odd_found = True
        for i in range(2, len(s) + 1):
            if i % 2 == 0 and even_found:
                for j in range(len(s) + 1 - i):
                    substring = s[j : j + i]
                    if substring == substring[::-1]:
                        even_found = True
                        longestPalindrome = substring
                        break
                    else:
                        even_found = False
            if i % 2 != 0 and odd_found:
                for j in range(len(s) + 1 - i):
                    substring = s[j : j + i]
                    if substring == substring[::-1]:
                        odd_found = True
                        longestPalindrome = substring
                        break
                    else:
                        odd_found = False
        return longestPalindrome

复杂度分析

时间复杂度O(n^{2}),步长逆序操作减少了回文判断的复杂度。

空间复杂度O(n)

 

再改进,通过判断切片起止字符是否相同,再进行回文判断,减少回文判断次数。达到64ms。复杂度并无改进。

class Solution(object):
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        if len(s) < 2 or s == s[::-1]:
            return s
        longestPalindrome = s[0]
        even_found = True
        odd_found = True
        for i in range(2, len(s)):
            if i % 2 == 0 and even_found:
                for j in range(len(s) + 1 - i):
                    if s[j] == s[j + i -1]:
                        substring = s[j : j + i]
                        if substring == substring[::-1]:
                            even_found = True
                            longestPalindrome = substring
                            break
                    else:
                        even_found = False
            if i % 2 != 0 and odd_found:
                for j in range(len(s) + 1 - i):
                    if s[j] == s[j + i -1]:
                        substring = s[j : j + i]
                        if substring == substring[::-1]:
                            odd_found = True
                            longestPalindrome = substring
                            break
                    else:
                        odd_found = False
        return longestPalindrome

 

再改进,原先以回文长度递增查找,但若先查奇数,则偶数可从最大的奇数长度+1开始,执行时间52ms。复杂度依旧无改进,但感觉缩减查找次数已经做到了极致。

class Solution(object):
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        if len(s) < 2 or s == s[::-1]:
            return s
        longestPalindrome = s[0]
        even_found = True
        odd_found = True
        max_even = 2
        for i in range(2, len(s), 2):
            if even_found:
                for j in range(len(s) + 1 - i):
                    if s[j] == s[j + i -1]:
                        substring = s[j : j + i]
                        if substring == substring[::-1]:
                            even_found = True
                            longestPalindrome = substring
                            max_even = i
                            break
                    else:
                        even_found = False
        for i in range(max_even + 1, len(s), 2):
            if odd_found:
                for j in range(len(s) + 1 - i):
                    if s[j] == s[j + i -1]:
                        substring = s[j : j + i]
                        if substring == substring[::-1]:
                            odd_found = True
                            longestPalindrome = substring
                            break
                    else:
                        odd_found = False
        return longestPalindrome

 

最后是在python提交执行时间靠前的算法上改进,达到了40ms,超越了百分百的提交。该种算法采用中心移动扩展,也是一种滑动窗口的方法。我对这种算法进行了分奇偶的改进,达到了目前最快执行时间。

先考虑偶数长度回文,想象字符串中每两个字符间的空隙为中心,则有n-1个中心,假设在某个中心查找2长度回文,若找到,则应向两边各扩展1查找4长度回文,但此时中心会自动增加1,则右边的扩展不需要管,而左边需要在原先的扩展基础上多扩展2,若找不到4长度回文,则什么也不用做,中心继续增大,继续查找4长度回文。

在找到的最长的偶数回文基础上,将偶数长度+1开始查找奇数长度,方法同上。

class Solution(object):
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        if len(s) < 2 or s == s[::-1]:
            return s

        length = 1

        even_medium = 0
        even_left = 0
        for i in range(len(s) - 1):
            if i - even_left >= 0 and s[i - even_left] == s[i + 1]:
                substring = s[i - even_left: i + 2]
                if substring == substring[: : -1]:
                    even_medium = i
                    length = even_left + 2
                    even_left = even_left + 2

        odd_medium = 0            
        half = (length + 1) // 2
        odd_left = (length + 1) // 2
        for j in range(half, len(s) - half):
            if j - odd_left >= 0 and s[j - odd_left] == s[j + half]:
                substring = s[j - odd_left: j + half + 1]
                if substring == substring[: : -1]:
                    odd_medium = j
                    length = odd_left + half + 1
                    odd_left = odd_left + 2

        if length % 2 == 0:
            return s[even_medium + 2 - length: even_medium + 2]
        else:
            if length == 1:
                return s[0]
            else:
                return s[odd_medium + half + 1 - length: odd_medium + half + 1]

复杂度分析

时间复杂度O(n),最多有2n-1个中心

空间复杂度O(1)

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值