5.最長回文子串(Swift版)

一墩划、題目

給定一個字符串 s涕刚,找到 s 中最長的回文子串。你可以假設(shè) s 的最大長度為 1000乙帮。
示例 1:
輸入: "babad"
輸出: "bab"
注意: "aba" 也是一個有效答案杜漠。
示例 2:
輸入: "cbbd"
輸出: "bb"

二、解題

首先用一個非常巧妙的方式察净,將所有可能的奇數(shù)/偶數(shù)長度的回文子串都轉(zhuǎn)換成了奇數(shù)長度:在每個字符的兩邊都插入一個特殊的符號驾茴。比如 abba 變成 #a#b#b#a#, aba變成 #a#b#a#氢卡。 為了進(jìn)一步減少編碼的復(fù)雜度锈至,可以在字符串的開始加入另一個特殊字符,這樣就不用特殊處理越界問題异吻,比如$#a#b#a#(注意裹赴,下面的代碼是用C語言寫就喜庞,由于C語言規(guī)范還要求字符串末尾有一個'\0'所以正好OK诀浪,但其他語言可能會導(dǎo)致越界)。

下面以字符串12212321為例延都,經(jīng)過上一步雷猪,變成了 S[] = "$#1#2#2#1#2#3#2#1#";

然后用一個數(shù)組 P[i] 來記錄以字符S[i]為中心的最長回文子串向左/右擴(kuò)張的長度(包括S[i],也就是把該回文串“對折”以后的長度)晰房,比如S和P的對應(yīng)關(guān)系:

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正好是原字符串中回文串的總長度)

那么怎么計算P[i]呢?該算法增加兩個輔助變量(其實(shí)一個就夠了殊者,兩個更清晰)id和mx与境,其中 id 為已知的 {右邊界最大} 的回文子串的中心,mx則為id+P[id]猖吴,也就是這個子串的右邊界摔刁。

然后可以得到一個非常神奇的結(jié)論,這個算法的關(guān)鍵點(diǎn)就在這里了:如果mx > i海蔽,那么P[i] >= MIN(P[2 * id - i], mx - i)共屈。就是這個串卡了我非常久。實(shí)際上如果把它寫得復(fù)雜一點(diǎn)党窜,理解起來會簡單很多:

//記j = 2 * id - i拗引,也就是說 j 是 i 關(guān)于 id 的對稱點(diǎn)(j = id - (i - id))
if (mx - i > P[j]) 
    P[i] = P[j];
else /* P[j] >= mx - i */
    P[i] = mx - i; // P[i] >= mx - i,取最小值幌衣,之后再匹配更新矾削。

當(dāng)然光看代碼還是不夠清晰,還是借助圖來理解比較容易。

當(dāng) mx - i > P[j] 的時候哼凯,以S[j]為中心的回文子串包含在以S[id]為中心的回文子串中垦细,由于 i 和 j 對稱,以S[i]為中心的回文子串必然包含在以S[id]為中心的回文子串中挡逼,所以必有 P[i] = P[j]括改,見下圖。


image.png

當(dāng) P[j] >= mx - i 的時候家坎,以S[j]為中心的回文子串不一定完全包含于以S[id]為中心的回文子串中嘱能,但是基于對稱性可知,下圖中兩個綠框所包圍的部分是相同的虱疏,也就是說以S[i]為中心的回文子串惹骂,其向右至少會擴(kuò)張到mx的位置,也就是說 P[i] >= mx - i做瞪。至于mx之后的部分是否對稱对粪,就只能老老實(shí)實(shí)去匹配了。

image.png

對于 mx <= i 的情況装蓬,無法對 P[i]做更多的假設(shè)著拭,只能P[i] = 1,然后再去匹配了牍帚。

于是代碼如下:

//輸入儡遮,并處理得到字符串s
int p[1000], mx = 0, id = 0;
memset(p, 0, sizeof(p));
for (i = 1; s[i] != '\0'; i++) {
    p[i] = mx > i ? min(p[2*id-i], mx-i) : 1;
    while (s[i + p[i]] == s[i - p[i]]) p[i]++;
    if (i + p[i] > mx) {
        mx = i + p[i];
        id = i;
    }
}
//找出p[i]中最大的

三、代碼示例

     class Solution {
        func longestPalindrome(_ s: String) -> String {
            if s.count <= 1 {
                return s
            }
            
            // 1.間隔之間先插入#
            var S = ["#"]
            for character in s {
                S.append(String(character))
                S.append("#")
            }
            
            // 2.遍歷找出以每個節(jié)點(diǎn)作為軸最長半徑
            var maxId:Int = 0
            var max:Int = 0
            var P:[Int] = [1]
            var maxLength:Int = 1
            var maxLengthIndex = 0
            
            for i in 1...S.count - 1 {
                // j是相對于maxId的i的左邊的對稱點(diǎn)
                let j:Int = maxId - (i - maxId)
                
                if max > i && j >= 0 {
                    // 優(yōu)化部分,請見文章
                    P.append(min(P[j], max - i))
                }else{
                    P.append(1)
                }
                // 循環(huán)判斷以i位置為中心的左右兩側(cè)是否相同,相同加1
                while i + P[i] <= S.count - 1 && i - P[i] >= 0 && S[i + P[i]] == S[i - P[i]]{
                    P[i] += 1
                }
                
                if i + P[i] - 1 > max {
                    // 以i為中心的子回文的最后一個元素的位置
                    max = i + P[i] - 1
                    // 記錄i為回文子串的中心id
                    maxId = i
                }
                
                // 判斷最長回文的長度,并記錄
                if P[i] > maxLength {
                    maxLength = P[i]
                    maxLengthIndex = i
                }
                print("i:\(i) maxId:\(maxId) max:\(max) maxLength:\(maxLength) maxLengthIndex:\(maxLengthIndex) P:\(P)")
            }
            let leftIndex = s.index(s.startIndex, offsetBy: (maxLengthIndex - (maxLength - 1))/2)
            let rightIndex = s.index(leftIndex, offsetBy:maxLength - 1 - 1)
            return String(s[leftIndex...rightIndex])
        }
    }

Demo地址:github
相關(guān)文章:Manacher's ALGORITHM: O(n)時間求字符串的最長回文子串

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末暗赶,一起剝皮案震驚了整個濱河市鄙币,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蹂随,老刑警劉巖十嘿,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異岳锁,居然都是意外死亡绩衷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門浸锨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來唇聘,“玉大人,你說我怎么就攤上這事柱搜〕倮桑” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵聪蘸,是天一觀的道長宪肖。 經(jīng)常有香客問我表制,道長,這世上最難降的妖魔是什么控乾? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任么介,我火速辦了婚禮,結(jié)果婚禮上蜕衡,老公的妹妹穿的比我還像新娘壤短。我一直安慰自己,他們只是感情好慨仿,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布久脯。 她就那樣靜靜地躺著,像睡著了一般镰吆。 火紅的嫁衣襯著肌膚如雪帘撰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天万皿,我揣著相機(jī)與錄音摧找,去河邊找鬼。 笑死牢硅,一個胖子當(dāng)著我的面吹牛蹬耘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播唤衫,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼婆赠,長吁一口氣:“原來是場噩夢啊……” “哼绵脯!你這毒婦竟也來了佳励?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤蛆挫,失蹤者是張志新(化名)和其女友劉穎赃承,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悴侵,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瞧剖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了可免。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抓于。...
    茶點(diǎn)故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖浇借,靈堂內(nèi)的尸體忽然破棺而出捉撮,到底是詐尸還是另有隱情,我是刑警寧澤妇垢,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布巾遭,位于F島的核電站肉康,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏灼舍。R本人自食惡果不足惜吼和,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望骑素。 院中可真熱鬧炫乓,春花似錦、人聲如沸献丑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阳距。三九已至塔粒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間筐摘,已是汗流浹背卒茬。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留咖熟,地道東北人圃酵。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像馍管,于是被迫代替她去往敵國和親郭赐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評論 2 361

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

  • 一個字符串的子串是字符串中連續(xù)的一個序列确沸,而一個字符串的子序列是字符串中保持相對位置的字符序列捌锭,譬如,"adi"可...
    AwesomeAshe閱讀 1,930評論 0 0
  • 上一篇KMP算法之后好幾天都沒有更新罗捎,今天介紹最長回文子串观谦。 首先介紹一下什么叫回文串,就是正著讀和倒著讀的字符順...
    zero_sr閱讀 2,303評論 2 8
  • 字符串最長回文子串 題目描述: 給定一個字符串桨菜,求它的最長回文子串的長度豁状。 分析和解法: 最容易想到的辦法是枚舉所...
    MinoyJet閱讀 654評論 0 2
  • 首先用一個非常巧妙的方式,將所有可能的奇數(shù)/偶數(shù)長度的回文子串都轉(zhuǎn)換成了奇數(shù)長度:在每個字符的兩邊都插入一個特殊的...
    Chris_C閱讀 153評論 0 0
  • Given a string s, find the longest palindromic substring ...
    stevewang閱讀 831評論 0 0