字符串系列之最长回文子串
2019獨角獸企業重金招聘Python工程師標準>>>
問題描述:
? ? 給定一個字符串S=A1A2...An,要求找出其最長回文子串(Longest Palindromic Substring)。所謂回文子串就是S的某個子串Ai...Aj為回文。例如,對字符串S=abcdcbeba,它的回文子串有:bcdcb,cdc,beb,滿足題目要求的最長回文子串為bcdcb。
推理思路:
1.由于回文可能由奇數個字符組成,也可能由偶數個字符組成。對奇數回文的處理比較直觀,只需要以某個字符為中心,依次向兩邊擴展即可。因此,我們可以通過如下方式把對偶數回文的處理轉換成對奇數回文的處理:在字符邊界添加特殊符號。例如,對字符串aba,預處理后變成#a#b#a#;對字符串abba,預處理后變成#a#b#b#a。可以看出,不管是奇數回文,還是偶數回文,在與處理后都變成奇數回文。在找出與預處理后字符串的最長回文后,只需要去除所有的#即為源字符串的最長回文。
2.對尋找字符串某類子串的問題,最簡單直觀的想法就是窮舉出所有子串一一進行判別。這里也不例外,當然時間復雜度也很高,為O(n^3)。
3.對該問題,我們可以進行一定程度的簡化處理。既然回文是一種特殊的字符串,我們可以以源字符串的每個字符為中心,依次尋找出最長回文子串P0, P1,...,Pn。這些最長回文子串中的最長串Pi = max(P1, P2,...,Pn)即為所求。請看源碼:
string?find_lps_native(const?string?&str) {int?center?=?0,?max_len?=?0;for(int?i?=?1;?i?<?str.length()-1;?++i){int?j?=?1;//以str[i]為中心,依次向兩邊擴展,尋找最長回文Piwhile(i+j?<?str.length()?&&?i-j?>=?0?&&?str[i+j]?==?str[i-j])++j;--j;if(j?>?1?&&?j?>?max_len){center?=?i;max_len?=?j;}}return?str.substr(center-max_len,?(max_len?<<?1)?+?1); }4.可以看出,上面做法的復雜度為O(n^2)。相比窮舉字符串的做法,已經降低了一個量級的復雜度。但是仔細想想,上面的算法還有改進空間嗎?當然有!而且改進后能夠把復雜度降低到O(n)!這就是大名鼎鼎的 Manacher’s Algorithm。請看下文分析:
? ? 舉例說明:對字符串S=abcdcba而言,最長回文子串是以d為中心,半徑為3的子串。當我們采用上面的做法分別求出以S[1]=a, S[2]=b, S[3]=c, S[4]=d為中心的最長回文子串后,對S[5]=c,S[6]=b...還需要一一進行擴展求嗎?答案是NO。因為我們已經找到以d為中心,半徑為3的回文了,S[5]與S[3],S[6]與S[2]...,以S[4]為對稱中心。因此,在以S[5],S[6]為中心擴展找回文串時,可以利用已經找到的S[3],S[2]的相關信息直接進行一定步長的偏移,這樣就減少了比較的次數(回想一下KMP中next數組的思想)。優化的思想找到了,我們先看代碼:
string?find_lps_advance(const?string?&str) {//find?radius?of?all?charactersvector<int>?p(str.length(),?0);int?idx?=?1,?max?=?0;for(int?i?=?1;?i?<?str.length()-1;?++i){if(max?>?i){p[i]?=?p[(idx?<<?1)?-?i]?<?(max?-?i)???p[(idx?<<?1)?-?i]:(max?-?i);}while(str[i+p[i]+1]?==?str[i-p[i]-1])p[i]?+=?1;if(i?+?p[i]?>?max){idx?=?i;max?=?i+p[i];}}//?find?the?character?which?has?max?radiusint?center?=?0,?radius?=?0;for(int?i?=?0;?i?<?p.size();?++i){if(p[i]?>?radius){center?=?i;radius?=?p[i];}}return?str.substr(center-radius,?(radius?<<?1)?+?1); }? ? 這里進行簡單的解釋:上述代碼中有三個主要變量,它們代表的意義分別是:
? ? ?p:以S[i]為中心的最長回文串的半徑為p[i]。
? ? ?idx:已經找出的能夠右延伸最遠距離的回文子串的起始位置。
? ? ?max:已經找出的能夠右延伸最遠距離的回文子串的結束位置。
? ? ?算法的主要思想是:先找出所有的p[i],最大的p[i]即為所求。在求p[j] (j>i)時,利用已經求出的p[i]減少比較次數。
? ? ?代碼中比較關鍵的一句是:
?p[i]?=?p[(idx?<<?1)?-?i]?<?(max?-?i)???p[(idx?<<?1)?-?i]:(max?-?i);? ? 在求p[i]時,如果max>i,則表明已經求出的最長回文中包含了p[i],那么與p[i]關于idx對稱的p[ (idx << 1) - i]的最長回文子串可以提供一定的信息。看了兩幅圖大概就明白什么意思了:
? ? 求二者的最小值是因為當前能夠獲取的信息都來自max的左側,需要進一步比較,求出以S[i]為中心的最長回文串。
5.除了上述的幾種做法外,還可以利用動態規劃以及后綴樹來進行求解。下次進行介紹。
? ? 結束行文之前,補充一句,對于字符串類的問題,建議多畫一畫,尋找其中的規律。
?
參考文獻:1.http://www.akalin.cx/longest-palindrome-linear-time
?
轉載于:https://my.oschina.net/pathenon/blog/63575
總結
以上是生活随笔為你收集整理的字符串系列之最长回文子串的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 孕妇梦到小黑蛇预示着什么
- 下一篇: 梦到去染头发是什么意思