5_Longest Palindromic Substring(Manacher) --LeetCode
參考:https://www.felix021.com/blog/read.php?2040,https://segmentfault.com/a/1190000002991199 做了修改。
首先用一個非常巧妙的方式,將所有可能的奇數/偶數長度的回文子串都轉換成了奇數長度:在每個字符的兩邊都插入一個特殊的符號。比如 abba 變成 #a#b#b#a#, aba變成 #a#b#a#。 為了進一步減少編碼的復雜度,可以在字符串的開始加入另一個特殊字符,這樣就不用特殊處理越界問題,比如$#a#b#a#(註意,下面的代碼是用C語言寫就,由於C語言規範還要求字符串末尾有一個‘\0‘所以正好OK,但其他語言可能會導致越界)。
下面以字符串12212321為例,經過上一步,變成了 S[] = "$#1#2#2#1#2#3#2#1#";
原帖中需要用一個數組 P[i] 來記錄以字符S[i]為中心的最長回文子串向左/右擴張的長度(包括S[i],也就是把該回文串“對折”以後的長度),比如S和P的對應關系:
S # 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #
P 1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1
(p.s. 可以看出,P[i]-1正好是原字符串中回文串的總長度)
但我不采用以上的做法,我的算法思想如下:
從S的下標1開始遍歷到下標S.length()-2做如下操作
1) 假設偏移量為offset, 用一個while循環控制在對比 S[i-offset] 和第 S[i+offset] 的字符是否相等,每次循環offset自增,直到S[i-offset] 不等於S[i+offset] 或者i-offset<1或者i+offset>S.length()-1跳出循環。
2) 使用一個變量maxlength記錄最長回文子串向左/右擴張的長度,以及一個maxidx記錄當前最大回文串的中心位置,判斷當前的offset與maxlength的大小,取較大賦值給maxlength。
3) 根據maxidx找到最大回文的中心位置,再根據其maxlength計算出回文串的起始位置以及長度。再使用substr()方法將回文串截取出來。如str.substr(maxidx-maxlength+1,2*maxlength-1);
4) 去掉其中的 ‘#’字符,就得到回文串。
代碼如下:
class Solution { public: string longestPalindrome(strings) { //小於1的字符串免處理,直接返回 if(s.size()<=1){ return s; } int len = s.size(); string str = preProcess(s); //使用一個變量maxlength記錄最長回文子串向左/右擴張的長度,以及一個maxidx記錄當前最大回文串的中心位置,判斷當前的offset與maxlength的大小,取較大賦值給maxlength。 int offset,maxidx = 0,maxlength = 0; for(int cur = 1; cur < str.size()-1; ++cur){ //查找最大回文串 offset = 0; while(cur-offset>=1 && cur+offset < str.size() && str[cur-offset] == str[cur+offset]) { ++offset; } //更新maxlength if(maxlength < offset){ maxlength = offset; maxidx = cur; } } //截取最長回文串 string longeststr = str.substr(maxidx-maxlength+1,2*maxlength-1); //去除"#"字符 string result = ""; for(int i = 0; i<longeststr.size();i++){ if(longeststr[i]!=‘#‘) result+=longeststr[i]; } return result; } private: //預處理 string preProcess(string s){ string str = "$"; int len = s.size(); for(int i = 0; i<len; i++) { str+="#"; str+=s[i]; } str+="#"; return str; } };
5_Longest Palindromic Substring(Manacher) --LeetCode