LeetCode_Everyday:005 Longest Palindromic Substring

LeetCode_Everyday:005 Longest Palindromic Substring


LeetCode Everyday:坚持价值投资,做时间的朋友!!!

题目:

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

示例:

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

代码

方法一:暴力搜索法

  • 思路核心:根据回文子串的定义,枚举所有长度大于等于2的子串,依次判断它们是否是回文
  • 实现要点:
    1. 在具体实现时,可以只针对大于“当前得到的最长回文子串长度”的子串进行“回文验证”;
    2. 在记录最长回文子串的时候,可以只记录“当前子串的起始位置”和“子串长度”,不必做截取。这一步我们放在后面的方法中实现。
  • 时间复杂度 O ( n 3 ) O(n^3) O(n3),空间复杂度 O ( 1 ) O(1) O(1)
执行用时 :7376 ms, 在所有 Python3 提交中击败了6.33%的用户
内存消耗 :13.7 MB, 在所有 Python3 提交中击败了9.26%的用户

class Solution1:
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """   
        size = len(s)
        if size < 2: return s
        max_len = 1
        res = s[0]
        
        for i in range(size - 1):
            for j in range(i+1, size):
                if j-i+1 > max_len and self.__valid(s, i, j):
                    max_len = j-i+1
                    res = s[i:j+1]
        return res
    
    def __valid(self, s, left, right):
        # 验证子串 s[left, right] 是否为回文串
        while left < right:
            if s[left] != s[right]:
                return False
            left += 1
            right -= 1
        return True
    
"""
For Example:    input:   s = 'babad'
               output:   'aba'
"""

s = 'babad'
                
solution = Solution1()
result = solution.longestPalindrome(s)
print('输出为:', result)   # 'bab'

方法二:动态规划

  • 思路核心:在头尾字符相等的情况下,里面子串的回文性质据定了整个子串的回文性质,这就是状态转移。
  • 实现要点:状态转移方程:dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1]
  • 时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( n 2 ) O(n^2) O(n2)
执行用时 :4436 ms, 在所有 Python3 提交中击败了36.66%的用户
内存消耗 :22.1 MB, 在所有 Python3 提交中击败了5.55%的用户

class Solution2:
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """   
        size = len(s)
        if size < 2: return s

        dp = [[False for _ in range(size)] for _ in range(size)]

        max_len = 1
        start = 0

        for j in range(1, size):
            for i in range(0, j):
                
                dp[i][j] = (s[i] == s[j]) and (j - i < 3 or dp[i + 1][j - 1])

                if dp[i][j]:
                    cur_len = j - i + 1
                    if cur_len > max_len:
                        max_len = cur_len
                        start = i
        return s[start:start + max_len]

"""
For Example:    input:   s = 'babad'
               output:   'aba'
"""

s = 'babad'
                
solution = Solution2()
result = solution.longestPalindrome(s)
print('输出为:', result)   # 'bab'

方法三:中心扩散法

  • 思路核心:枚举可能出现的回文子串的“中心位置”,从“中心位置”尝试尽可能扩散出去,得到一个回文串。
  • 实现要点:扩散的时候奇偶性是不同的,分开来写。
  • 时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( 1 ) O(1) O(1)
执行用时 :1104 ms, 在所有 Python3 提交中击败了74.43%的用户
内存消耗 :13.7 MB, 在所有 Python3 提交中击败了9.26%的用户

class Solution3:

    def expandAroundCenter(self, s, left, right):
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1
        return left + 1, right - 1

    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """   
        start, end = 0, 0
        for i in range(len(s)):
            left1, right1 = self.expandAroundCenter(s, i, i)
            left2, right2 = self.expandAroundCenter(s, i, i + 1)
            if right1 - left1 > end - start:
                start, end = left1, right1
            if right2 - left2 > end - start:
                start, end = left2, right2
        return s[start: end + 1]
    
"""
For Example:    input:   s = 'babad'
               output:   'aba'
"""

s = 'babad'
                
solution = Solution3()
result = solution.longestPalindrome(s)
print('输出为:', result)   # 'bab'

方法四:搜索改进法

  • 思路核心:我认为这是第一种方法的升级版本。
  • 实现要点:相当于第二轮搜索只搜索可能是最长子串的两种情况。
  • 时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( 1 ) O(1) O(1)
执行用时 :76 ms, 在所有 Python3 提交中击败了98.46%的用户
内存消耗 :13.6 MB, 在所有 Python3 提交中击败了9.26%的用户

class Solution4:
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """   
        res = ''
        for i in range(len(s)):
            start = max(0, i-len(res)-1)
            temp = s[start : i+1]
            if temp == temp[::-1]:
                res = temp
            else:
                temp = temp[1:]
                if temp == temp[::-1]:
                    res = temp
        return res

"""
For Example:    input:   s = 'babad'
               output:   'aba'
"""

s = 'babad'
                
solution = Solution3()
result = solution.longestPalindrome(s)
print('输出为:', result)   # 'aba'

方法五:Manacher算法

  • 思路核心:Manacher 算法本质上还是中心扩散法,只不过它使用了类似 KMP 算法的技巧,充分挖掘了已经进行回文判定的子串的特点,在遍历的过程中,记录了已经遍历过的子串的信息,也是典型的以空间换时间思想的体现。
  • 实现要点:这部分可以参考 题注
  • 时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( 1 ) O(1) O(1)
执行用时 :140 ms, 在所有 Python3 提交中击败了92.75%的用户
内存消耗 :13.5 MB, 在所有 Python3 提交中击败了9.26%的用户

class Solution5:
    
    def expand(self, s, left, right):
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1
        return (right - left - 2) // 2

    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """   
        end, start = -1, 0
        s = '#' + '#'.join(list(s)) + '#'
        arm_len = []
        right = -1
        j = -1
        for i in range(len(s)):
            if right >= i:
                i_sym = 2 * j - i
                min_arm_len = min(arm_len[i_sym], right - i)
                cur_arm_len = self.expand(s, i - min_arm_len, i + min_arm_len)
            else:
                cur_arm_len = self.expand(s, i, i)
            arm_len.append(cur_arm_len)
            if i + cur_arm_len > right:
                j = i
                right = i + cur_arm_len
            if 2 * cur_arm_len + 1 > end - start:
                start = i - cur_arm_len
                end = i + cur_arm_len
        return s[start+1:end+1:2]

"""
For Example:    input:   s = 'babad'
               output:   'aba'
"""

s = 'babad'
                
solution = Solution3()
result = solution.longestPalindrome(s)
print('输出为:', result)   # 'bab'

测试

  • 提交后的执行用时,只能作为参考,它和很多因素有关,包括数据的维度,数据的特殊性,机器的性能等等
  • 而且同样的时间复杂度,由于具体情况的不同,每个复杂度乘以的比例,其他未算入的低阶项等等都会带来不一样的时间损耗
  • 甚至同一个算法的不同实现,实际用时都是不一样的
  • 实际工程也是需要权衡各种利弊,具体问题具体分析

s = 'aaaaa'*1000为例,测试结果如下:

solutionsolution1solution2solution3solution4solution5
time1.61s5.28s2.26s16.7ms11.5ms

参考

  1. https://leetcode-cn.com/problems/longest-palindromic-substring/solution/zui-chang-hui-wen-zi-chuan-by-leetcode-solution/
  2. https://leetcode-cn.com/problems/longest-palindromic-substring/solution/zhong-xin-kuo-san-dong-tai-gui-hua-by-liweiwei1419/
  3. https://www.bilibili.com/video/BV1d7411U7dw?from=search&seid=112685129946594939

此外

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值