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(n3)O(n^3),空間複雜度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(n2)O(n^2),空間複雜度O(n2)O(n^2)
執行用時 :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(n2)O(n^2),空間複雜度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(n2)O(n^2),空間複雜度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(n2)O(n^2),空間複雜度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爲例,測試結果如下:

solution solution1 solution2 solution3 solution4 solution5
time 1.61s 5.28s 2.26s 16.7ms 11.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

此外

  • 原創內容轉載請註明出處
  • 請到我的GitHub點點 star
  • 關注我的 CSDN博客
  • 關注公衆號:CV伴讀社

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章