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 };