LeetCode 5

題目

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

樣例

  • Example 1

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

    Input: "cbbd"
    
    Output: "bb"
    

思想

這里來說一下O(n)來解決最長回文串的問題,即Manacher算法

那么首先需要說一個O(n2)的方法饶囚,然后才能體會O(n)的方法的真正巧妙的地方在哪里走哺。O(n2)的方法是枚舉一個中心點i,然后向左右兩邊查找肺樟,直到發(fā)現(xiàn)Str[i-k]與Str[i+k]不相等了,或者是越界了,再開始下一次中心點的枚舉关筒。然后對于"baab"這樣沒有中心點的對稱回文描孟,又要用相同的方法再做一次(中心點變成兩個)驶睦。

大家就會發(fā)現(xiàn)如果遇到了像"cccccc"砰左,這樣多次重復(fù)的串,那么一個位置就會被重復(fù)訪問多次场航,這就是它很慢的根本原因缠导,并且奇偶分別解決的話也非常的麻煩。那么我們就需要想辦法來規(guī)避掉這兩個問題溉痢。對于搜索狀態(tài)的重復(fù)性的解決方案一般就是利用DP(對于無后效性的問題)僻造。那么我們就使用DP的思想再來思考一下這個問題,就會發(fā)現(xiàn)有一些狀態(tài)是可以被繼承的孩饼。因為這是回文串髓削,那么就會出現(xiàn)a與b關(guān)于c成鏡像的情況,進一步在以c為中心的回文串當(dāng)中镀娶,以a為中心的回文串和以b為中心的回文串一定是相同的立膛。(這里沒看懂不要緊,后面會具體畫圖分析)

那么我們就利用這個特性梯码,就能解決掉重復(fù)訪問的問題宝泵。對于奇偶的問題,我們就可以使用插入'#'號的方法來規(guī)避掉轩娶。(這個辦法真是太巧妙了)

baab => #b#a#a#b#
bacab => #b#a#c#a#b#

這樣就把奇偶的回文串全部轉(zhuǎn)化成了奇數(shù)的回文串儿奶。

好了,那下面進入正題罢坝,怎么解決掉重復(fù)訪問的問題廓握?

  • 我們可以先證明一個命題:RL[i]表示在變形字符串中以i為中心的回文串的半徑,RL[i]-1是在原字符串中以Str[i]為中心的最長回文串的長度
例如:bacab => #b#a#c#a#b#
原字符串的長度為5嘁酿,變形后的長度為11隙券。
RL[6] = 6,即在變形串中以c為中心的最長回文串的半徑為6闹司。
那么整個回文串的長度應(yīng)為L = 2 * RL[6] -1娱仔。
因為該回文串首尾必定為'#',所以隨便去掉首部或者尾部的'#'后游桩,該回文串為原回文串長度的2倍牲迫。
所以原回文串長度L` = [2 * RL[6] - 2] / 2 = RL[6] - 1
  • 那么我們的任務(wù)也就是快速計算RL數(shù)組了,這里就需要用到前面提到的思想借卧。首先我們來設(shè)幾個值盹憎,maxRight(現(xiàn)在能觸及到的最右邊的位置)pos(觸及到最右邊位置時回文中心的index)铐刘,i(計算RL數(shù)組的枚舉變量)陪每。
    • i <= maxRight :
      • 這時我們觀察上圖可以發(fā)現(xiàn)在兩個紅格子之間的所有格子都是關(guān)于pos對稱的,也就是i也有關(guān)于pos對稱的點。那我們可以計算出i關(guān)于pos的對稱點j = 2 * pos - i檩禾。
      • 這時候又要分為兩種情況挂签,第一種是pos - j + 1> RL[j],即j的回文串半徑?jīng)]有超過MaxRight的鏡像盼产。這時就很簡單了饵婆,因為都是關(guān)于pos對稱的,那么i的回文串和j的回文串也是對稱的戏售。RL[i] = RL[j]
      • 那第二種情況侨核,j的回文串半徑超過了MaxRight的鏡像。那么i只能繼承j在這個鏡像區(qū)域的部分蜈项,并且繼承之后需要繼續(xù)向兩邊擴展芹关。這里需要注意因為需要繼續(xù)擴展续挟,那么就很有可能會刷新maxRight值和pos值紧卒。


    • i > maxRight :
      • 這種情況就很簡單了,因為i已經(jīng)超過了maxRight诗祸,那么就不能繼承鏡像的任何東西跑芳。就只能自己向兩邊擴展了。這里也可能會刷新maxRight的pos的值直颅。

以上圖片均來自網(wǎng)上博客

代碼

  public int extendIndex(char[] charArr, int i, int l, int r) {
    while (l - 1 >= 0 && r + 1 < charArr.length) {
      if (charArr[l - 1] == charArr[r + 1]) {
        l --;
        r ++;
      } else {
        break;
      }
    }
    return (i - l + 1);
  }

  public String longestPalindrome(String s) {
    char[] _s = s.toCharArray();
    char[] charArr = new char[_s.length * 2 + 1];
    int[] f = new int[charArr.length];
    int id = -1, maxRight = -1, ansId = 0, max = -1;
    charArr[0] = '#';
    for (int i = 1; i <= _s.length; i++) {
      charArr[i * 2 - 1] = _s[(i - 1)];
      charArr[i * 2] = '#';
    }

    for (int i = 0; i < charArr.length; i++) {
      if (i > maxRight) {
        f[i] = extendIndex(charArr, i, i, i);
      } else {
        // i 關(guān)于id的鏡像
        int j = 2 * id - i;
        if (i + f[j] - 1 >= maxRight) {
          f[i] = extendIndex(charArr, i, 2 * i - maxRight, maxRight);
        } else {
          f[i] = f[j];
        }
      }
      if (i + f[i] - 1 > maxRight) {
        maxRight = i + f[i] - 1;
        id = i;
      }
      if (f[i] > max) {
        max = f[i];
        ansId = i;
      }
    }

    char[] res = new char[(max - 1)];
    int index = 0;
    for (int i = -max + 1; i < max; i++) {
      if (charArr[ansId + i] != '#') {
        res[index] = charArr[ansId + i];
        index ++;
      }
    }
    String resStr = String.valueOf(res);
    return resStr;
  }

代碼不夠精簡博个,后續(xù)會更新一個精簡版

復(fù)雜度分析

以上進行了算法的分析,那最后分析一下這種算法的復(fù)雜度和在O(n^2)算法的基礎(chǔ)上的提高功偿。

  • 關(guān)于Manacher算法的時間復(fù)雜度分析具體可以參見:知乎-如何證明Manacher算法的時間復(fù)雜度是O(n)?
  • 我僅僅簡單說說我的想法盆佣,在每一次循環(huán)當(dāng)中會涉及到一個問題,maxRight會不會被向右推動械荷,如果maxRight不被向右推動共耍,那么這次循環(huán)O(1)能夠完成。如果maxRight被向右推動吨瞎,理論上是O(n)才能完成痹兜,但是因為能進行繼承,所以這個值遠小于n颤诀,并且當(dāng)maxRight達到n時字旭,整個算法結(jié)束。所以n會被分散在每次推動的復(fù)雜度中的崖叫。
  • 現(xiàn)在再回過頭來看整個算法遗淳,主要是在O(n^2)算法的基礎(chǔ)之上利用了繼承前面的計算結(jié)果來減少一個元素的多次訪問問題。并且很巧妙的利用了回文串的鏡像原理心傀。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屈暗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恐锦,老刑警劉巖往果,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異一铅,居然都是意外死亡陕贮,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門潘飘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肮之,“玉大人,你說我怎么就攤上這事卜录「昵埽” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵艰毒,是天一觀的道長筐高。 經(jīng)常有香客問我,道長丑瞧,這世上最難降的妖魔是什么柑土? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮绊汹,結(jié)果婚禮上稽屏,老公的妹妹穿的比我還像新娘。我一直安慰自己西乖,他們只是感情好狐榔,可當(dāng)我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著获雕,像睡著了一般薄腻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上典鸡,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天被廓,我揣著相機與錄音,去河邊找鬼萝玷。 笑死嫁乘,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的球碉。 我是一名探鬼主播蜓斧,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼睁冬!你這毒婦竟也來了挎春?” 一聲冷哼從身側(cè)響起看疙,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎直奋,沒想到半個月后能庆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡脚线,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年搁胆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邮绿。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡渠旁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出船逮,到底是詐尸還是另有隱情顾腊,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布挖胃,位于F島的核電站杂靶,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏冠骄。R本人自食惡果不足惜伪煤,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望凛辣。 院中可真熱鬧,春花似錦职烧、人聲如沸扁誓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蝗敢。三九已至,卻和暖如春足删,著一層夾襖步出監(jiān)牢的瞬間寿谴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工失受, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留讶泰,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓拂到,卻偏偏與公主長得像痪署,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子兄旬,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,685評論 2 360

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