【LeetCode】5.Longest Palindromic Substring 最長迴文子串問題

題目:

           Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.


翻譯:

           給定一個字符串,找出它的最大回文子串,返回。


分析:

          所謂”迴文“,是指倒着讀和正着讀結果都一樣的字符串,例如”abcba"或“abba"。

         這個題目有很多種解法,下面將依次進行分析。


        1.Brute force (暴力破解法)

         即依次得到給定字符串的每一個子串,判斷這個子串是否爲迴文串,找到最長迴文子串。

         暴力破解法獲取每個子串用了兩個for循環(O(n^2)),在判斷該子串是否爲迴文時又用了一個while循環(O(n)),所以時間複雜度爲O(n^3)。

        代碼如下,這段代碼在OJ上是沒法通過的,因爲當字符串很長時會超時。

class Solution {
public:
    string longestPalindrome(string s) {
         int startLP=0;//記錄最長迴文子串的起始位置
        int maxLen=0;//記錄最長迴文子串的長度
        for(int i=0;i<s.length();i++)
            for(int j=i+1;j<s.length();j++)
            {
                int start=i,end=j;
                while(start<=end&&s[start]==s[end])//判斷該子串是否爲迴文
                {
                    start++;
                    end--;
                }
                if(start>end)//若該子串爲迴文子串
                {
                    if((j-i+1)>maxLen)
                    {
                        startLP=i;
                        maxLen=j-i+1;
                    }
                }
            }
        return s.substr(startLP,maxLen);
    }
};

       2.Dynamic Programming(動態規劃)

      動態規劃是暴力法的一個進化版本,我們沒必要每一次都重新判斷一個子串是否爲迴文串,可以將一些輔助判斷的信息記錄下來供後來者使用,這樣後面的迴文串判斷就僅需O(1)的時間,而不是暴力法中的O(n)。若字符串從i..j的子串是迴文,則字符串總(i+1)...(j-1)的子串也是迴文,由此最長迴文子串就可以分解成一序列子問題。

      定義P[i, j]=1表示子串s[i,..., j]是迴文串,P[i, j]=0表示子串s[i,..., j]是不是迴文串,則得到狀態方程和轉移方程爲:

      

       因此,使用動態規劃的方法來求解需要額外O(n^2)空間,時間複雜度也爲O(n^2)

       在具體實現中,由於判斷長度爲n的子串是否爲迴文串需要藉助長度爲(n-2)的子串是否爲迴文串來判斷,因此自底向上進行計算,初始化長度爲1和長度爲2的子串的P[i, j],然後依次計算長度爲3,4,... ,s.length()的子串的P[i, j],計算過程同時更新最長迴文子串的起始位置和長度。代碼如下:

 

class Solution {
public:
    string longestPalindrome(string s) {
        int P[s.length()][s.length()]={0};
        int startLP=0;//記錄最長迴文子串起始位置
        int maxLen=0;//記錄最長迴文子串長度
        for(int i=0;i<s.length();i++)
        {
            P[i][i]=1;
            startLP=i;
            maxLen=1;
        }
        for(int i=0;i<s.length()-1;i++)
        {
            if(s[i]==s[i+1])
            {
                P[i][i+1]=1;
                startLP=i;
                maxLen=2;
            }
        }
        for(int len=3;len<=s.length();len++)
        {
            for(int i=0;i<s.length()-len+1;i++)
            {
                int j=i+len-1;
                if(s[i]==s[j]&&P[i+1][j-1]==1)
                {
                    P[i][j]=1;
                    startLP=i;
                    maxLen=len;
                }
            }
        }
        return s.substr(startLP,maxLen);
    }
};
      (這裏有一個小疑惑,一樣的思路,我用C#寫的時候,當字符串過長時會發生Memory Limit Exceeded,但是重新用C++寫了之後,就不出現這樣的問題了,這是爲什麼?好吧,原諒我這放蕩不羈的編程語言,一會兒用這,一會兒用那~)


      3. 中心擴展法       

          中心擴展法的基本思想是依次以每一個字符爲中心,向兩邊擴展,查找以這個字符未中心的最長迴文串,從而找到最終的最長迴文串。需要注意的是迴文串長度爲單數和雙的情況,例如:“abcba"和”abba",因此在向兩邊擴展時需要考慮以該字符爲中心的擴展,以及以該字符及其下一個字符爲中心的擴展,這樣才能包含迴文串長度爲雙數的情況。

       中心擴展法需要遍歷一遍字符串對每一個字符進行擴展,擴展的時間複雜度爲O(n),由此得到中心擴展法的時間複雜度爲O(n^2),空間複雜度爲O(1)。代碼如下:

class Solution {
public:
    string expansion(string s,int start1,int start2)
    {
        int l=start1,r=start2;
        while(l>=0&&r<s.length()&&s[l]==s[r])
        {
            l--;
            r++;
        }
        return s.substr(l+1,r-1-l);
    }
    
    string longestPalindrome(string s) {
         if(s.length()==1)
            return s;
         string lp="";
         for(int i=0;i<s.length()-1;i++)
         {
            string tempStr=expansion(s,i,i);
            if(tempStr.length()>lp.length())
                lp=tempStr;
            tempStr=expansion(s,i,i+1);
             if(tempStr.length()>lp.length())
                lp=tempStr;
         }
         return lp;
    }  
};
 

      4.Manacher算法

      ......明天繼續


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