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.

Example:
Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.

Example:
Input: "cbbd"
Output: "bb"

題目簡介

這道題輸入是一個(gè)字符串尿褪,要求輸出最長的回文子串凉倚。

知識要點(diǎn)

  1. 動態(tài)規(guī)劃的思想
  2. 對稱的思想

解題思路

Approach 1: Brute Force

暴力法應(yīng)該是最樸素的算法了:我們查找所有的子串搏熄,判斷是否為回文并記錄最長回文子串腾它。這種算法筆者在此不列代碼注暗。
關(guān)于時(shí)間復(fù)雜度,尋找所有子串為O(n2),判斷是否為回文為O(n)滚局,所以總的時(shí)間復(fù)雜度為O(n3)。

Approach 2: Dynamic Programming

這道題應(yīng)用動態(tài)規(guī)劃來解題應(yīng)該還是很容易想到的顽频。設(shè)置一個(gè)dp布爾型二維數(shù)組藤肢,其中dp[i][j]表示字符串從第i位置到第j位置(包括i,j)是否為回文。那么很容易得到狀態(tài)轉(zhuǎn)移方程:<code>if ( dp[i+1][j-1] == true && s[i] == s[j] ) dp[i][j] = true;</code> dp數(shù)組初始化操作見代碼中pre-process部分糯景。
c++代碼如下:

class Solution {
public:
    //dynamic programming
    string longestPalindrome(string s) {
        int len = s.length();
        bool dp[1010][1010] = {0};
        int i, j;
        int maxL=1, start=0, tmpL;
        
        //pre-processing
        for (i=0; i<len; ++i){
            dp[i][i] = true;
            j = i + 1;
            if (j < len && s[i] == s[j]){
                dp[i][j] = true;
                maxL = 2;
                start = i;
            }
        }
        
        //dynamic programming
        for (tmpL=3; tmpL<=len; ++tmpL){
            for (i=0; i+tmpL-1<len; ++i){
                j = i+tmpL-1;
                if (dp[i+1][j-1] && s[i]==s[j]){
                    dp[i][j] = true;
                    maxL = tmpL;
                    start = i;
                }
            }
        }
        
        //output
        string lp = s.substr(start, maxL);
        return lp;
    }
};

時(shí)間復(fù)雜度為O(n^2)

Approach 3: Expand Around Center

第三種算法被稱為中心擴(kuò)展法嘁圈。核心思想為:從左到右遍歷字符串,從“中心”向兩邊擴(kuò)展直到不再是回文蟀淮。該算法要注意的是“中心”的選擇:有兩種情況最住,“aba”和“abba”。前者中心為一個(gè)字符怠惶,后者中心為兩個(gè)字符涨缚。我們對兩種情況分別進(jìn)行處理。
C++代碼如下:

class Solution {
public:
    //expand around center
    string longestPalindrome(string s) {
        int len = s.length();
        int maxL = 1, start = 0, tmpL;
        int i, j, k;
        
        //aba type
        for (i=0; i<len; ++i){
            j = i-1;
            k = i+1;
            tmpL = 1;
            while(j>=0 && k<len && s[j]==s[k]){
                tmpL += 2;
                if (tmpL > maxL){
                    maxL = tmpL;
                    start = j;
                }
                j--;
                k++;
            }
        }
        
        //abba type
        for (i=0; i<len; ++i){
            j = i;
            k = i+1;
            tmpL = 0;
            while(j>=0 && k<len && s[j]==s[k]){
                tmpL += 2;
                if (tmpL > maxL){
                    maxL = tmpL;
                    start = j;
                }
                j--;
                k++;
            }
        }
        
        //output
        string lp = s.substr(start, maxL);
        return lp;
    }
};

時(shí)間復(fù)雜度為O(n^2)

Approach 4: Manacher's Algorithm

這個(gè)算法算是一個(gè)比較冷門的算法了策治。不過其思想非常有趣脓魏,還是很有理解透徹的價(jià)值的。網(wǎng)上的眾多文章對該算法的解釋并不是很清晰通惫,筆者覺得manacher's algorithm這篇文章算是解釋最好的一篇了茂翔,轉(zhuǎn)過來參考一下。
筆者依據(jù)該算法的思想自己用C++實(shí)現(xiàn)了一下:

class Solution {
public:
    //manacher's algorithm
    //add # to the string 
    string pre_process(string s){
        string pps = "#";
        for (int i=0; i<s.length(); ++i){
            pps += s.substr(i, 1);
            pps += "#";
        }
        return pps;
    }
    
    //find the maximum value in p[], which is the radius of the longest palindrome. Return the palindromic string.
    string post_process(string s, int p[], int n){
        int center, radius=0;
        for (int i=0; i<n; ++i){
            if (p[i] > radius){
                radius = p[i];
                center = i;
            }
        }
        string ans = "";
        for (int i=center-radius; i<=center+radius; ++i){
            if (s[i] != '#')
                ans += s.substr(i, 1);
        }
        return ans;
    } 
     
    string longestPalindrome(string s){
        s = pre_process(s); 
        int p[2010]; //the radius of palindromic substring, not including character in i
        int maxlen = 0, maxi = 0;
        p[0] = 0;
        
        //different situations of manacher's algorithm
        for (int i=1; i<s.length(); ++i){
            // i >= maxlen, the point is out of the range
            if (i >= maxlen){
                p[i] = 0;
                while ( i-(p[i]+1)>=0 && i+(p[i]+1)<s.length() && s[i-(p[i]+1)] == s[i+(p[i]+1)] )
                    ++p[i];
                if (i+p[i] > maxlen){
                    maxi = i;
                    maxlen = maxi+p[i];
                }
            }
            // i < maxlen, the point is within the range
            else{
                int j = 2*maxi - i; // the symmetric point
                if (j - p[j] < maxi - p[maxi])
                    p[i] = maxlen - i;
                else if (j - p[j] > maxi - p[maxi])
                    p[i] = p[j];
                else{
                    p[i] = p[j];
                    while ( i-(p[i]+1)>=0 && i+(p[i]+1)<s.length() && s[i-(p[i]+1)] == s[i+(p[i]+1)] )
                        ++p[i];
                    if (i+p[i] > maxlen){
                        maxi = i;
                        maxlen = maxi+p[i];
                    }
                }
            }
        }
        string ansstr = post_process(s, p, s.length());;
        return ansstr;
    }
};

該算法時(shí)間復(fù)雜度達(dá)到了O(n)
對代碼的一些說明:

  1. pre-process函數(shù)是為字符串沒個(gè)字符之間添加‘#’履腋,使得“aba”型和“abba”型能夠統(tǒng)一處理珊燎。
  2. post-process函數(shù)是輸出處理。尋找最大回文半徑遵湖,并將‘#’刪除掉俐末。返回最終的輸出。
  3. 算法的主體部分的思想?yún)⒖?a target="_blank" rel="nofollow">manacher's algorithm這篇文章奄侠。

參考

  1. http://blog.csdn.net/xingyeyongheng/article/details/9310555
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市载矿,隨后出現(xiàn)的幾起案子垄潮,更是在濱河造成了極大的恐慌,老刑警劉巖闷盔,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弯洗,死亡現(xiàn)場離奇詭異,居然都是意外死亡逢勾,警方通過查閱死者的電腦和手機(jī)牡整,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來溺拱,“玉大人逃贝,你說我怎么就攤上這事谣辞。” “怎么了沐扳?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵泥从,是天一觀的道長。 經(jīng)常有香客問我沪摄,道長躯嫉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任杨拐,我火速辦了婚禮祈餐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘哄陶。我一直安慰自己帆阳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布奕筐。 她就那樣靜靜地躺著舱痘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪离赫。 梳的紋絲不亂的頭發(fā)上芭逝,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機(jī)與錄音渊胸,去河邊找鬼旬盯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛翎猛,可吹牛的內(nèi)容都是我干的胖翰。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼切厘,長吁一口氣:“原來是場噩夢啊……” “哼萨咳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起疫稿,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤培他,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后遗座,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舀凛,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年途蒋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了猛遍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖懊烤,靈堂內(nèi)的尸體忽然破棺而出梯醒,到底是詐尸還是另有隱情,我是刑警寧澤奸晴,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布冤馏,位于F島的核電站,受9級特大地震影響寄啼,放射性物質(zhì)發(fā)生泄漏逮光。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一墩划、第九天 我趴在偏房一處隱蔽的房頂上張望涕刚。 院中可真熱鬧,春花似錦乙帮、人聲如沸杜漠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽驾茴。三九已至,卻和暖如春氢卡,著一層夾襖步出監(jiān)牢的瞬間锈至,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工译秦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留峡捡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓筑悴,卻偏偏與公主長得像们拙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子阁吝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容