1. 程式人生 > >Leetcode 5. Longest Palindromic Substring 最長迴文子串

Leetcode 5. Longest Palindromic Substring 最長迴文子串

Leetcode 5. Longest Palindromic Substring 最長迴文子串

標籤: Leetcode


題目地址:https://leetcode-cn.com/problems/longest-palindromic-substring/

題目描述

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

示例 1:

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

輸入: “cbbd”
輸出: “bb”

演算法思想

暴力法

雖然說是暴力法,但是我也對其做了一點點優化,並不是完全就是尋找所有子串,然後判斷子串是否為迴文。

這裡是在遍歷字串的時候,把遍歷到的字元儲存到一個字典裡面,然後遍歷到下一個字元的時候先看看字元是否在字典裡面,因為如果是長度大於2的迴文串的話,這個串的首尾元素肯定是一樣的,所以如果不在的話,可定不是迴文。

如果在的話,就進行判斷這個字元到裡面存在的字元之間的這個子串是否為迴文的,並且記錄其長度與當前最長子串的長度比較。

這裡有個地方要注意的是,字典在儲存的時候,要把每個字元出現的所有位置都記錄下來比如:"abaca",記錄a的話要記錄為{"a":[0,]} ,這樣的原因是由於每次出現的子串並不確定到哪個才是最長的。所以在檢測的時候也要把所有位置的子串都檢測一遍。

python程式碼

class Solution(object):
    def check(self,s,start,end):
        i,j = start,end
        while(i<j):
            if s[i] !=s[j]:
                return ""
            i+=1
            j-=1
        return s[start:end+1]
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
# 最短也是一個 best = s[0] if s!="" else "" dict = {} for i in range(len(s)): if s[i] not in dict: dict[s[i]] = [i] else: for start in dict[s[i]]: temp = self.check(s,start,i) if len(temp)>len(best): best = temp dict[s[i]].append(i) return best

這個程式碼雖然能夠執行,但是提交的時候回出現超時。所以就找其他的方法解決。

動態規劃法

對於可以進行動態規劃的問題,其實關鍵就在於找到那個動態規劃的算式。找到規劃的規律,然後我們先解出小規模的問題的解,然後大規模問題的解就根據算式計算出來,所以一般動態規劃都需要一個一維或者二維的矩陣,用於儲存小規模問題的解,這樣的話我們大規模問題的解可以在此基礎上計算得到,所以也算是一種用空間換取時間的策略。

對於本題來說,對於字串str,我們使用dp[i][j]=1表示str[i:j]是迴文子串,那麼可以推得dp[i+1][j-1]=1,因為迴文串把首尾刪了肯定還是迴文串,所以我們根據迴文串把首尾刪了還是迴文串得到如果一個迴文串加上一個首尾一樣的字元那麼構成的串還是迴文串,所以得到狀態轉移方程:

d p [ i ] [ j ] = { d p [ i + 1 ] [ j 1 ] s t r [ i ] = s t r [ j ] 0 s t r [ i ] s t r [ j ] dp[i][j]= \begin{cases} dp[i+1][j-1] &amp;&amp; str[i]=str[j] \\ 0 &amp;&amp; str[i]\ne str[j] \\ \end{cases}

在剛開始的時候我們可以定義所有的dp[i][i] =1,因為單個元素一定是迴文,並且還有dp[i][i+1] =1 在str[i]=str[i+1],因為兩個相等的子串也一定是迴文的。

由於題目是要我們輸出一個子串,所以我們在進行構造轉移方程的時候記得記錄一下最長子串的開始位置以及長度,這樣方便最終返回。

舉個栗子

如有子串str = "babad"

則初始dp為

` 0 1 2 3 4
0 1 0
1 1 0
2 1 0
3 1 0
4 1

其實在做完初始矩陣我們就是完成了一個最長長度最多為2的計算,從這個表格明顯可以看出沒有長度為2的迴文子串,我們下面所需要做得就是按照狀態方程,把整個表格的右上角填滿,按照對角線的方式填,比如下次計算應該計算長度為3的了,那麼計算dp[0][2],因為str[0]=str[2],所以dp[0][2]=dp[0+1][2-1] = 1,可以看出其為迴文從0開始長度為3的為迴文串,所以我們也可以更新一下最長子串的標記start = 0 longest = 3,這樣方便我們以後返回這個子串。

最終dp為:

` 0 1 2 3 4
0 1 0 1 0 0
1 1 0 1 0
2 1 0 0
3 1 0
4 1

python程式碼實現


class Solution(object):
    
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        #動態規劃 
        #ref https://www.cnblogs.com/mini-coconut/p/9074315.html
        length = len(s)
        #最長子串開始位置
        start = 0
        #最長子串長度  --所以最長子串為 s[start,longest]
        longest = 1
        d = [[0]*length for i in range(length)]
        for i in range(length):
            d[i][i] = 1
            if i+1 <length:
                if s[i] ==s[i+1]:
                    d[i][i+1] =1
                    start = i
                    longest = 2
        # 構造陣列右三角,按對角線構造,就是每次外層for遍歷的為一個斜的對角線
        for l in range(3,length+1):
            for i in range(0,length+1-l):
                j = i+l-1
                if s[i] == s[j] and d[i+1][j-1]:
                    d[i][j] = 1
                    start = i
                    longest = l
        return s[start:start+longest]

參考:

https://www.cnblogs.com/mini-coconut/p/9074315.html