1. 程式人生 > >LeetCode-5. Longest Palindromic Substring(最長迴文子串)

LeetCode-5. Longest Palindromic Substring(最長迴文子串)

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

Example 1:

Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.

Example 2:

Input: "cbbd"
Output: "bb"

方法一:暴力解法

容易想到的方法往往耗時。思路是醬紫噠:一段字串可能的最短迴文是單個字元,可能的最長迴文是它本身。那我們就設定一個視窗,長度從1到 len(s)。若有視窗長度w,依次以每個字元為起點,取長度為w的字串,注意最後一個起點不可能是最後一個字元,否則視窗就填不滿了。

判斷所取的字串和它的反轉字串是否相同,若相同這個字串就是迴文。記錄各次查詢的最長迴文。時間複雜度為O(n³),程式碼中雖然只有兩重for迴圈,但第三次其實是b = a[::-1],這個反轉過程是迴圈產生的。

#Python3
class Solution:
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        l = 0;ans = "";a = b =""
        for w in range(1,len(s)+1):    #w為視窗長度
            for i in range(0,len(s)-w+1):    #每個視窗的起始位置,最後一個起點應是最後一個視窗的開頭
                a = s[i:w+i];b = a[::-1]    #取這段視窗的字串即其逆置字串
                if a == b:    #如果這個視窗內的字串和它的逆置字串相等,則它是迴文
                    if len(a) > l:    #如果這段迴文比之前的迴文長,那把這段迴文作為最新答案
                        l = len(a)
                        ans = a
        return ans

方法二:動態規劃

這個方法是對暴力解法的一個改進。暴力解法每次都要取字串並進行判斷,相當於把所有情況逐一列舉出來。但動態規劃對每次進行判斷時,只要知道上一次是什麼情況就可以了,也就是說只要我知道一個初始狀態,我就可以從這個初始狀態出發,根據一定的規則,依次推匯出後面各個狀態。首先定義 P(start,end),對於字串s[start,end],如果它是迴文,那麼P(start,end)為True,否則為False。那怎麼判斷s[start,end]是否是迴文?如果把該字串兩端去掉後,剩下的部分即s[start+1,end-1]是迴文,那麼只要s[start]==s[end],即新加上的兩端相同,那麼s[start,end]一定是迴文。但是長度為1和2時,不能用上面的方法,長度為2時,假設我們求P(1,2),那麼s[start+1,end-1]為s[2,1],實際情況不可能出現。

對於p[start][end] = (w == 1 or w == 2 or p[start+1][end-1]) and (s[start] == s[end]),長度為1時,肯定是迴文,長度為2時,因為要同時滿足s[start] == s[end],所以長度為2也是迴文。演算法時間複雜度是O(n²)。

#Python
class Solution(object):
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        maxL = 0;l = len(s);ans = ""    #maxL記錄最大長度
        p = [[False for _ in range(l)]for _ in range(l)]    #生成二維陣列
        for w in range(1,l+1):    #從1開始取視窗大小
            for start in range(0,l):    #設定起點
                end = start + w - 1
                if end >= l:    #如果終點超出所給字元總長,不合要求,結束本次迴圈
                    break
                p[start][end] = (w == 1 or w == 2 or p[start+1][end-1]) and (s[start] == s[end])    #判斷條件
                if p[start][end] and w > maxL:    #如果是迴文且比之前的迴文都長
                    maxL = w;ans = s[start:end+1]
        return ans
//C++
class Solution {
public:
    string longestPalindrome(string s) {
        int len = s.length();
        if(len == 0) return "";
        bool p[len][len];memset(p, false, sizeof(p));    //memset()對p進行初始化
        int maxL = 0;
        string ans = "";
        for(int w = 1;w <= len;w++){
            for(int start = 0;start < len;start++){
                int end = start + w - 1;
                if(end >= len)
                    break;
                p[start][end] = (w == 1 || w == 2 || p[start+1][end-1]) && (s[start] == s[end]);
                if(p[start][end] && w > maxL){
                    maxL = w;
                    ans = s.substr(start,w);
                }
            }
        }
        return ans;
    }   
};

其實官方總共給了5中解法,但是我覺得還是要根據自己的情況從自己能掌握的開始。後面孰能生巧了,再去掌握那些更高階的演算法。