馬拉車演算法manacher

2021-10-20 17:48:57 字數 3712 閱讀 2142

1. 預處理解決奇回文和偶回文問題

比如 str = "bcbaa",在每個字元的開頭,結尾和中間插入乙個特殊字元「#」來得到乙個新的字串

「#b#c#b#a#a#」, 這樣對於原來字串中的奇回文「bcb」來說,在新的字串中變成了「#b#c#b#」,還是奇回文,只是回文串長度從3變成了7;

注意**中( i & 1) == 0,與1按位與,如果i是偶數則結果為0,否則為1,因為偶數的二進位制表示為 *******0,最後一位為0,而1的二進位制表示為00000001,按位與之後肯定全0,而奇數的二進位制表示為*******1,與1按位與之後結果還是1。

這樣處理過後,字串中如果存在回文串,則所有回文串都是奇回文!

2. 三個輔助變數

str經過預處理之後記為chararr,對chararr中的每個字元都進行拓展,但不再是盲目的中心拓展,而是對每個字元拓展時,都會參考已經拓展過的結果。

陣列parr。其長度與chararr一樣,parr[i]的意義是以字元chararr[i]為回文中心時,擴出去得到的最大回文白半徑是多少。

回文半徑就是回文串長度整除2再加1,加1是因為算上了回文中心。

整數pr。意義是之前遍歷的所有字元的所有回文子串中,最右邊即將到達的位置。其實就是所有回文子串中,最右到達的字元位置再加1。

需要注意的是,當pr已經到了字串的右邊界的時候,就不用再更新了,因為不可能再有更右邊的邊界了。問題是,這個時候某個子串右邊界已經到達了chararr的右邊界了,再以右邊的字元為中心進行拓展,其中心串長度也不能再增長了啊?還要繼續往下算嗎?

整數index。表示最近一次pr更新時,對應的回文中心的位置。每次更新pr就更新index,當pr不再更新,index也不再更新。

3. 從左到右一次計算parr陣列每個位置的值

得到的最大值就是chararr中最大回文半徑,然後再把這個長度對應回str中的最大回文半徑,然後即可求得最大回文子串

(1)假設現在計算到了以chararr[i]為中心的子串,在i位置之前的計算中,會不斷更新pr(回文子串最後即將到達的位置)和index(最右即將到達位置的回文子串對應的回文中心)的值。分兩種情況進行chararr[i]值的計算。

(2)pr-1位置即前邊拓展過的回文子串中已經到達的最遠位置,沒有包含當前要計算的i位置。這個時候就是普通的中心拓展,沒有獲得加速。

(3)pr-1位置包含了當前的i位置。如下邊圖示

對於上邊這個例子,其實不太恰當吧,pr為11的時候,以位置6-10為中心的回文子串長度已經不可能超過pr對應的那個子串的長度了,如果是要求最大回文串的話,則問題已經結束了吧。但是如果要求字串中所有的回文串的個數,則問題還是要繼續的。

圖中位置i是要計算回文半徑的位置。右大是pr-1的位置,左大是右大以index對稱的位置。則從左大到右大組成了乙個回文串,稱為大回文串。

已知i之前位置的parr即回文半徑都已經計算過了,以index為對稱中心得到i對稱的位置i',那麼位置i'的回文半徑也一定是計算過的。假設以i』為中心的最大回文串的左邊界和右邊界分別記為「左小」和「右小」,很明顯左小處與右小處的字元相等。則根據左小右小與左大右大的位置關係可以分三種情況來討論位置i處回文半徑的計算。

分類標準是左小在左大的左邊、右邊,以及左小剛好等於左大三種情況!

注意左小到右小和左大到右大是兩個回文串!

情況一:「左小」和「右小」完全包含在「左大」和「右大」內部,此時以位置i為中心的最大回文半徑可直接確定,就是位置i'處的回文半徑(已計算)

a'是左小的前乙個字元,b'是右小的後乙個字元,因為左小到右小是以i'為中心的最大回文串,所以a' != b',否則以i'為中心的回文串會更大

把a',b',左小,右小以index為中心對稱過去如圖所示。因為左小到右小時回文串,右小'到左小'是它的逆序,當然也是回文串。而a'!=b',則a也不等於b,因此位置i處的最大回文半徑與位置i'處一樣,可直接確定。

情況二:左小到右小這個字串的左側部分在左大到右大字串的外部,即小字串左小左大在左邊超出左大這個邊界了,此時位置i處回文半徑可直接確定。

注意這裡的abcd跟情況一不是乙個含義,情況一中a'是左小的前乙個字元,b'是右小的後乙個字元,而情況二中的ab與左小右小沒有關係,只與大字串左大到右大有關係。

a是左大左邊第乙個字元,左大』是左大以i'為中心的對稱位置。同理右大'是右大以i為中心的對稱位置。

b是左大』右邊第乙個字元,c是右大』左邊第乙個字元,d是右大右邊第乙個字元。

因為左大到左大』包含在左小到右小之間,而左小到右小是回文串,因此左大到左大』也是回文串,因此右大』到右大也是回文串。

則右大』到右大就是位置i對應的最大回文串。

證明:因為a和b都包含在左小到右小之間,所以a == b成立

又因為b和c關於index對稱,包含在左大到右大之間,所以b==c成立

又因為index處的最長回文串就是左大到右大,所以a != d,否則index處的回文串會更長

綜上,c != d

所以右大』到右大就是位置i處的最長回文串

情況三:左小和左大是同乙個位置,以i'為中心的最大回文串剛好壓在以index為中心的最大回文串的邊界上,此時只能在中心拓展時減少運算,不能直接確定位置i處的回文半徑。

如圖,左小左大重疊,右大』與右大與左小左大對稱,則以位置i為中心的回文串至少是右大』到右大

設a為右大』左側第乙個字元,b為右大右側第乙個字元,關於a和b是否相等我們是無法判斷的,因此要從右大'左側和右大的右側字元分配向左向右進行拓展。才能最終確定位置i處的最大回文半徑。

4. 得到陣列parr[i],求最大回文串長度

設位置i處回文半徑最大,parr[i]為max,則max - 1就是原字串str中最長回文子串。

設在字串chararr中的最長回文串長度為m,則 max = m/2 +1,這裡的出發是整除,加1是加上中心字元。

設此最長回文串對應str中回文串長度為n,則m = 2n+1

故 (2n+1)/2 + 1 = max 這裡得除法也是整除

得 n =max -1

高階問題一 求字串中所有回文子串的個數

注意,如果乙個位置i處的最大回文半徑是x,則這個位置i處的所有回文子串的個數就是x,所以如果要求乙個字串中所有的回文子串,則把parr陣列中所有元素加起來即可。

高階問題二 在字串最後新增最少字元,使整個字串成為回文串

在必須包含最後乙個字元的情況下,查詢最長的回文子串,然後把字串前邊不在這個回文子串內的子字串逆序接在後邊,就能使得整個字串成為回文串。

Manacher 馬拉車演算法

給定乙個字串,求出其的最長回文子串 乙個將時間複雜度優化到o n 的演算法 暴力演算法,但不是純暴力,即按照做過的事情不再去做來優化 我們知道,乙個回文串要麼是奇數的串 aba 要麼是偶數的串 abba 可以看出,乙個回文串有乙個對稱軸 對於奇數串aba來說,對稱軸就是b 而對於偶數串abba來說,...

Manacher演算法(馬拉車)

演算法總結第三彈 manacher演算法,前面講了兩個字串相演算法 kmp和拓展kmp,這次來還是來總結乙個字串演算法,manacher演算法,我習慣叫他 馬拉車 演算法。相對於前面介紹的兩個演算法,manacher演算法的應用範圍要狹窄得多,但是它的思想和拓展kmp演算法有很多共通支出,所以在這裡...

Manacher演算法 馬拉車

於 馬拉車的解決的問題 給定字串s,求s中的最長回文子串?解釋 回文串就是正讀反讀都一樣的字串,比如奇回文串 bab 偶回文串 noon 馬拉車演算法步驟 index01 2345 6789 1011 1213 char 1 2 2 1 2 2 r121 2521 6123 21規律 最大半徑減1等...