[Leetcode] 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.

做這道題之前要先了解什么是回文子串。回文串通俗的解釋是,分別從字符串兩端開始遍歷,得到的結果相同,如“abba”,從兩端的遍歷結果都是:“abba”。

判斷一個字符串是否為回文串的思路是使用兩個指針從,兩端開始向中間遍歷,看是否對應的字符是否相等,直到兩指針相等或者交叉。

方法一:以字符串中的每一個字符為中心遍歷字符串。

思路:本題的解決方法是從頭到尾的遍歷字符串S,以每個字符為中心,向兩端不停的擴展,同時判斷以當前字符為中心的兩端字符是否相等,在判斷的過程中找到最大的回文串長度,並記錄下回文串開始的最左端,這樣就找到了最大的回文串。但是這樣做就遇到了一個問題:“abcab”這種個數為奇數的回文串可以計算出來,但是若是"abba"這樣個數為偶數的情況,該如何計算?遇到這種情況,則,可以判斷相鄰兩字符是否相等來判斷是否為回文串,針對s=“abba”,我們發現 i=1時,s[i]==s[i+1],然后同時向兩端擴,發現s[ 0]=s[ 2],這樣該串也為回文串,所以,在判斷以某字符為中心的時候,要分兩種情況,即,回文串中字符的個數為奇數和為偶數的情況。時間復雜度是O(n*n)。代碼如下:

 1 class Solution {
 2 public:
 3     string longestPalindrome(string s) 
 4     {
 5         string res="";
 6         int len=s.size();
 7         if(len==1)  return s;
 8         int maxLen=0,curLen=0,sbegin;
 9 
10         for(int i=0;i<len;++i)
11         {
12             //奇數
13             int left=i-1,right=i+1;
14             while(left>=0&&right<len&&s[left]==s[right])
15             {
16                 curLen=right-left;
17                 if(curLen>maxLen)
18                 {
19                     maxLen=curLen;
20                     sbegin=left;
21                 }
22                 left--,right++;
23             }
24 
25             //偶數
26             left=i,right=i+1;
27             while(left>=0&&right<len&&s[left]==s[right])
28             {
29                 curLen=right-left;
30                 if(curLen>maxLen)
31                 {
32                     maxLen=curLen;
33                     sbegin=left;
34                 }
35                 left--,right++;
36             }
37         }
38         res=s.substr(sbegin,maxLen+1);  //substring()為前閉后開
39         return res;
40     }
41 };

 

方法二:馬拉車算法Manacher's Algorithm,有點是:將時間復雜度降低為O(n)的地步。關於馬拉車算法Manacher's Algorithm,詳情見博客Manacher's Algorithm詳解。這里給出代碼:

 

 1 class Solution {
 2 public:
 3     string longestPalindrome(string s) 
 4     {
 5         //重新構造新的字符串t,這樣新的字符串的字符個數始終為奇數個
 6         string t="&#";  //加&是為了防止越界,后面自帶'\0'
 7         for(int i=0;i<s.size();++i)
 8         {
 9             t+=s[i];
10             t+="#";
11         }
12         //新建P[i]用來存放以t[i]字符為中心的回文子串的半徑
13         vector<int> P(t.size(),0);
14 
15         int mx=0//是回文串能延伸到的最右端位置
16         int iD=0//每個回文串的中間點
17         int resLen=0,resCenter=0;
18         for(int i=1;i<t.size();++i)
19         {   
20             /*當i<mx時,能對稱取值得下標,當半徑小於mx-i時,就是本身,
21              *大於則只能取mx-iD,超過的部分只能++的比較。當i>mx時,只能++的比較。*/
22             p[i]=mx>i?min(p[2*iD-i],mx-i):1;    
23 
24             while(t[i+P[i]]==t[i-P[i]])
25                 ++P[i];           //以i點為中心的P[i]的最大值
26 
27             if(mx<i+P[i])           //重新選擇中心點並改變mx
28             {
29                 mx=i+P[i];
30                 iD=i;
31             }
32             if(resLen<P[i])         //記錄最大的半徑和對應的中間點的值
33             {
34                 resLen=P[i];
35                 resCenter=i;
36             }
37 
38         }
39         return s.substr((resCenter-resLen)/2,resLen-1);
40     }
41  };

 


免責聲明!

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



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