字符串 - 最長(zhǎng)回文子串

給定一個(gè)字符串 s,找到 s 中最長(zhǎng)的回文子串捻浦。

示例1:

輸入: "babad"
輸出: "bab"
注意: "aba" 也是一個(gè)有效答案隘膘。

示例2:

輸入: "cbbd"
輸出: "bb"

解決方法很多刀森,其中有一種時(shí)間復(fù)雜度達(dá)到了O(n)級(jí)別逝薪,就是Manachar算法隅要。在思想上很類似于KMP,同樣是利用已知信息減少重復(fù)操作董济。

一. 情況分析與預(yù)處理

  • 可以發(fā)現(xiàn)的是步清,回文串有兩種情況
    • 奇數(shù)回文:aba回文中心是一個(gè)字符。
    • 偶數(shù)回文:bb回文中心是兩個(gè)字符中間虏肾。
  • 為了簡(jiǎn)化操作(兩種情況分別處理)廓啊,在每個(gè)字符左右添加特殊字符#,將兩種情況合并為奇數(shù)封豪。
    babad化為# b # a # b # a # d #
    cbbd化為 # c # b # b # d #
    這時(shí)候回文中心必定是一個(gè)字符谴轮。

二. 如何獲取最長(zhǎng)回文子串長(zhǎng)度?

這里需要借助一個(gè)輔助數(shù)組p撑毛,用于記錄以每個(gè)字符為中心的最大回文半徑书聚。

  • 輔助數(shù)組與預(yù)處理后的字符串?dāng)?shù)組等長(zhǎng)。
  • 輔助數(shù)組對(duì)應(yīng)位置的值存放以該位置字符為中心的最長(zhǎng)回文子串長(zhǎng)度藻雌。(回文子串只有一個(gè)字符時(shí)為1)
index 0 1 2 3 4 5 6 7 8 9 10
arr   # b # a # b # a # d #
p     1 2 1 4 1 4 1 2 1 2 1 

得到兩個(gè)最大值(4)雌续,代表著以index=3index=5兩個(gè)字符為中心的最大回文子串半徑都為4(這個(gè)長(zhǎng)度包含了特殊字符)。我們?nèi)コ厥庾址蟀l(fā)現(xiàn)這兩個(gè)子串為bababa胯杭,長(zhǎng)度為3驯杜。

  • 在測(cè)試更多例子后我們可以發(fā)現(xiàn)這樣一個(gè)規(guī)律:最大的半徑p[i]與最長(zhǎng)回文子串長(zhǎng)度maxLength存在這樣一種關(guān)系
    • const maxLength = p[i] -1

二. 如何獲取最長(zhǎng)回文子串?

回歸題目做个,顯然獲取到最大長(zhǎng)度是不夠的鸽心,還需要知道開始的index。
let res = slice(最長(zhǎng)回文開始index居暖,index + maxLength)
那么顽频,如何獲取這個(gè)開始索引呢?Manachar算法給出了一個(gè)巧妙的方法太闺。

  • 偶數(shù)回文情況
index 0 1 2 3 4 5 6 7 8
arr   # c # b # b # d #
p     1 2 1 2 3 2 1 2 1 

最大半徑index為4糯景,p[index] = 3,index - p[index] = 1省骂。在cbbdindex = 1剛好就是最長(zhǎng)回文bb的開始位置蟀淮。

  • 奇數(shù)回文情況
index 0 1 2 3 4 5 6 7 8 9 10
arr   # b # a # b # a # d #
p     1 2 1 4 1 4 1 2 1 2 1 

最大半徑index為3,p[index] = 4钞澳,index - p[index] = -1怠惶。數(shù)組越界。

初步結(jié)論:最長(zhǎng)回文子串開始索引 = index - p[index]轧粟,但是奇數(shù)情況下不成立策治。
  • 為了解決越界問題脓魏,我們?cè)赼rr前面和后面再添加一對(duì)特殊符號(hào)$@(必須成對(duì)加)
index 0 1 2 3 4 5 6 7 8 9 10 11 12
arr   $ # b # a # b # a # d  #  @  
p       1 2 1 4 1 4 1 2 1 2  1  

再次使用剛才的結(jié)論,最大半徑index為4览妖,p[index] = 4轧拄,index - p[index] = 0揽祥,剛好是babad的最長(zhǎng)回文bab的開始索引讽膏。

  • 奇數(shù)回文問題解決,加了兩個(gè)符號(hào)后偶數(shù)回文的情況:
index 0 1 2 3 4 5 6 7 8 9 10
arr   $ # c # b # b # d # @
p       1 2 1 2 3 2 1 2 1 

最大半徑index為5拄丰,p[index] = 3府树,index - p[index] = 2,結(jié)論需要修正料按,即(index - p[index]) / 2奄侠。(奇數(shù)情況下 / 2 不影響)

最終結(jié)論:最長(zhǎng)回文子串的開始索引 = (index - p[index]) / 2

三. 計(jì)算輔助數(shù)組p

  • 計(jì)算p是整個(gè)算法最核心的地方,也不太好理解载矿。
  • 設(shè)置兩個(gè)指針:
    • center:取得tail的回文子串的中心
    • tail:已知的最長(zhǎng)回文子串的最右端
  • 整個(gè)過程是一次數(shù)組遍歷垄潮,假如我們現(xiàn)在要算p[i](也就是前面的0 <= j < i都已經(jīng)算完了)。
    1. 情況1:i < tail
    • i 仍然處于已知最長(zhǎng)子串的范圍內(nèi)闷盔,由于回文的對(duì)稱性弯洗,以i為回文中心的子串在左邊能夠找到對(duì)應(yīng)的j子串。
      其中:j = 2 * center - i
    • 但是需要注意的是逢勾,i為中心的回文子串不一定整串都處于已知的center子串范圍內(nèi)牡整,也就是說有可能從右邊延展出去。在這種情況下溺拱,我們姑且認(rèn)為i的子串最多到tail位置了逃贝,大于tail的部分需要逐一匹配。這種時(shí)候迫摔,我們暫時(shí)認(rèn)為p[i] = tail - i
      上面兩個(gè)結(jié)論綜合得到p[i] = Math.min(p[2*center - i],tail - i)
    1. 情況2:i > tail
      當(dāng)i大于tail時(shí)沐扳,無法根據(jù)已知信息推斷i為中心的子串長(zhǎng)度,需要從中心開始擴(kuò)展句占。

四. JavaScript實(shí)現(xiàn)

function Manacher(str){
    if(str.length < 2){
        return str;
    }

    // 1.預(yù)處理沪摄,添加'#'以及前后兩個(gè)特殊字符'$'、'@'
    let s = '$';
    for(let i = 0;i < str.length;i++){
        s += `#${str[i]}`;
    }
    s += '#@';

    // 2.初始化輔助量
    let center = 0;
    let tail = 0;
    let p = [];
    let maxLength = -1;// 最長(zhǎng)回文子串的長(zhǎng)度
    let index = 0;// 最長(zhǎng)回文子串的中心位置

    // 2-1.遍歷s
    for(let j = 1;j < s.length - 1;j++){

        // 直接使用i < tail情況下的結(jié)論
        p[j] = tail > j ? Math.min(p[2*center - j],tail - j) : 1;

        // 中心向兩邊擴(kuò)展
        while(s[j - p[j]] == s[j + p[j]]){
            p[j]++;
        }

        // 如果新的回文串最右端大于tail辖众,就需要更新tail與center
        if(tail < p[j] + j){
            tail = p[j] + j;
            center = j;
        }

        // 如果行的回文串大于maxLength卓起,那么就更新maxLength
        if(maxLength < p[j] - 1){
            maxLength = p[j] - 1;
            index = j;
        }

    }

    let begin = (index - maxLength) / 2;

    return str.slice(begin,begin + maxLength);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市凹炸,隨后出現(xiàn)的幾起案子戏阅,更是在濱河造成了極大的恐慌,老刑警劉巖啤它,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奕筐,死亡現(xiàn)場(chǎng)離奇詭異舱痘,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)离赫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門芭逝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人渊胸,你說我怎么就攤上這事旬盯。” “怎么了翎猛?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵胖翰,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我切厘,道長(zhǎng)萨咳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任疫稿,我火速辦了婚禮培他,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘遗座。我一直安慰自己舀凛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布员萍。 她就那樣靜靜地躺著腾降,像睡著了一般。 火紅的嫁衣襯著肌膚如雪碎绎。 梳的紋絲不亂的頭發(fā)上螃壤,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音筋帖,去河邊找鬼奸晴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛日麸,可吹牛的內(nèi)容都是我干的寄啼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼代箭,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼墩划!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嗡综,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤乙帮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后极景,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體察净,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡驾茴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了氢卡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锈至。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖译秦,靈堂內(nèi)的尸體忽然破棺而出峡捡,到底是詐尸還是另有隱情,我是刑警寧澤诀浪,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布棋返,位于F島的核電站,受9級(jí)特大地震影響雷猪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜晰房,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一求摇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧殊者,春花似錦与境、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至海蔽,卻和暖如春共屈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背党窜。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工拗引, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人幌衣。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓矾削,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親豁护。 傳聞我的和親對(duì)象是個(gè)殘疾皇子哼凯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

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