【Leetcode刷題】最長回文子串


主要記錄解題過程,反思如何構思代碼。

原題:https://leetcode-cn.com/problems/longest-palindromic-substring

題目:

給定一個字符串 s,找到 s 中最長的回文子串。你可以假設 s 的最大長度為 1000。

示例 1:

輸入: "babad"
輸出: "bab"
注意: "aba" 也是一個有效答案。
示例 2:

輸入: "cbbd"
輸出: "bb"

解題過程

看到這題一開始是完全懵逼的,看着兩個例子想了一個錯的解法:用兩個指針指向字符串的首尾,當兩個指針所指的內容相同時,記錄下兩個索引值,不同時索引值歸零,直到兩個指針相遇。

這個解法很明顯是錯的,因為它假定了最長回文子串的中心一定是字符串中心

這個時候我看到了題目的三條提示:

How can we reuse a previously computed palindrome to compute a larger palindrome?

If “aba” is a palindrome, is “xabax” and palindrome? Similarly is “xabay” a palindrome?

Complexity based hint:
If we use brute-force and check whether for every start and end position a substring is a palindrome we have O(n^2) start - end pairs and O(n) palindromic checks. Can we reduce the time for palindromic checks to O(1) by reusing some previous computation.

前兩條提示非常有用,我想到回文的特征是:呈中心對稱。即,兩個指針以同樣的步幅從回文的中心出發,所指內容應該會一直保持一致。應該還是使用兩個指針,但是是從內到外走,而不是從外到內

總體算法思路為:遍歷整個字符串,將每個位置當作回文中心去找以這個字符為中心能形成的最長回文,然后選出整個字符串最長的回文

這里有一個很干擾我思路的問題出現了:就是'cbbd'這種情況。我的算法是無法輸出'bb'的,在這個地方卡了很久。后來我決定把這種情況特殊處理,如果第i個字符串與第i+1個字符串相同,則將尾指針往后移。

我覺得有一個經驗就是,在時間緊迫的時候,先把自己能想出來的完整算法寫出來,再想辦法去處理特殊情況,否則在那空想是很浪費時間的。

第一版:

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        longest_palindrome = {'length': 0, 'str': ''}
        for i in range(n):
            index1 = index2 = i
            if i+1 < n and s[i+1] == s[i]:
                index1 = i
                index2 = i+1
            while index1-1 >= 0 and index2+1 < n and s[index1-1] == s[index2+1]:
                index1 += -1
                index2 += 1
            length = index2 - index1 + 1
            if length > longest_palindrome['length']:
                longest_palindrome['length'] = length
                longest_palindrome['str'] = s[index1:index2+1]
        return longest_palindrome['str']

結果:不通過

不通過的用例:

輸入:
"ccc"
輸出:
"cc"
預期:
"ccc"

在第二個c的位置,由於第三個c與第二個c相等,則index1=1,index2=2,將回文的中心變成了這兩個字符,所以出錯。

解決的思路是:找全所有所有相同的字符組成一個回文中心,即在處理第一個c的時候,就一直往后找,直到回文中心變為"ccc"

第二版:

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        longest_palindrome = {'length': 0, 'str': ''}
        for i in range(n):
            index1 = index2 = i
            while index2+1 < n and s[index1] == s[index2+1]:
                index2 += 1
            while index1-1 >= 0 and index2+1 < n and s[index1-1] == s[index2+1]:
                index1 += -1
                index2 += 1
            length = index2 - index1 + 1
            if length > longest_palindrome['length']:
                longest_palindrome['length'] = length
                longest_palindrome['str'] = s[index1:index2+1]
        return longest_palindrome['str']

可以再優化一下,不要每次找到更長的回文就切出來,只需要記下索引就好了

第三版:

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        l_len = 0
        l_index1 = 0
        l_index2 = 0
        for i in range(n):
            index1 = index2 = i
            while index2+1 < n and s[index1] == s[index2+1]:
                index2 += 1
            while index1-1 >= 0 and index2+1 < n and s[index1-1] == s[index2+1]:
                index1 += -1
                index2 += 1
            length = index2 - index1 + 1
            if length > l_len:
                l_len = length
                l_index1 = index1
                l_index2 = index2
        return s[l_index1: l_index2+1]

二刷

就因為加了“下一次mid的選擇越過相同的字符”這一個操作,執行用時從800ms降到90ms

應該是測試用例中有很多連在一起的相同字符

class Solution:
    def longestPalindrome(self, s: str) -> str:
        # 遍歷s,將每個字符當作回文中心擴散
        n = len(s)
        if n == 0:
            return ""
        # 記錄最長的回文子串,初始為第一個字符
        res = s[0]
        # 回文中心
        mid = 0
        while mid < n:
            # left和right指向回文子串的前一個字符和后一個字符
            left, right = mid - 1, mid + 1
            # 找全所有相同的字符組成回文中心
            while right < n and s[right] == s[mid]:
                right += 1
                # 下一次mid的選擇應該越過這些相同的字符
                mid += 1
            while left >= 0 and right < n and s[left] == s[right]:
                left -= 1
                right += 1
            # 當前回文串的長度
            if right - left - 1 > len(res):
                res = s[left+1:right]
            mid += 1
        return res

時間復雜度:O(n2)。最多有n個回文中心,每個回文中心最多向外拓展n次

空間復雜度:O(1)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM