manacher演算法筆記

2022-01-17 07:08:22 字數 3063 閱讀 9789

暑假上的字串課沒聽懂幾個字,所以現在像在學新演算法一樣

好像就是在學新演算法,不管了

其實講真的字串的演算法都比較巧妙啊!kmp,ac自動機,manacher,sam,這些演算法都是思想非常巧妙的,而且**比較短!所以 很難,學不會 要好好理解

這是本蒟蒻字串專題學習的第乙個演算法,為什麼不是kmp?因為題單第一道題目就是用的manacher(bushi .

(圖暫時沒放,有時間畫了就放上來)

好了扯談結束.

oi中一直有乙個經典的問題,求回文子串。

然後大概有三種演算法來解決這個問題:

這裡講的就是manacher演算法.

為什麼上面兩個它們不行?

第乙個不能處理偶回文(找不到中心!)而且時間複雜度o(\(n^2\)),

第二個時間複雜度o(\(n^2\)),顯然也不大行的樣子

但是我們的manacher演算法,它的時間複雜度為o(n),並且可以適應奇回文和偶回文!這個演算法大大滴好!

所以我們用manacher 來解決問題.

演算法思想:

將偶回文串轉化為奇回文串,同時利用已經處理過的資訊,借助對稱(回文串具有對稱的性質)來對當前處理的點進行擴充套件。

定義\(r\)是當前擴充套件到的所有回文串的最右邊界

定義\(pos\) 是右邊界為\(r\)且第一次擴充套件到\(r\)的時候對應的中心點

定義\(l\)是\(r\)關於\(pos\)的對稱點(也就是以\(pos\)為對稱中心的回文串的左端點),\(l = 2* pos - r\)

定義\(x\)的回文半徑是我們求到的以\(x\)為中心的最長回文串的左邊界到\(x\)的長度,規定為\(p[x]\)

定義\(i\)是當前處理到的,要求以\(i\)為中心的回文半徑

演算法流程:

第一步,先把偶回文轉化為奇回文,這個的處理就是在原串的基礎上,每兩個字元中間加上乙個奇奇怪怪的字元

例如原串為: str = "a , b , b , a , b , a "

轉化後就為:str' = "^ , a , # , b , # , b , # , a , # , b , # , a , $ "

放張:

你數一數是不是偶回文就變成了奇回文了呀?

細心的小夥伴已經注意到了,第乙個加入的字元和最後乙個加入的字元不一樣 !這個是為了處理邊界,後面會用到

第二步,對於新串中的每乙個點求它們的回文半徑.

按照前面的規定,我們現在知道了\(r\)以及\(pos\)

,還有當前我們要求回文半徑的點為\(i\)

,同時顯然\(i > pos\),而且我們已經知道了擴充套件後的字串的\(i\)前面字元的所有\(p[j]\),\((0 <= j < i)\)

我們規定\(i\)關於\(pos\)對稱的點為\(i'\) , \(i' = 2 * pos - i\)

然後分情況討論:

那麼\(i'\)就會大於\(l\)小於\(pos\)

\(1.\)如果\(p[i'] + i <= r\),這就告訴我們,以\(i'\)為中心的回文串被包含在回文串\([l,r]\)(也就是當前右端點最右的回文串).

那麼因為\([l,r]\)是乙個回文串,這個串\([l,r]\) 就具有對稱性,那麼\(p[i] = p[i']\)

\(2.\) 如果\(p[i'] + i > r\),這就告訴我們,以\(i'\)為中心的回文串 一定不是 完全包含在回文串\([l,r]\)中。

但是至少我們可以知道,以\(i'\)為中心的回文串包含在回文串\([l,r]\)中間的部分,根據對稱性,以\(i\)為中心的回文串的長度一定大於等於這部分,然後我們再進行暴力擴充套件.

綜合上面兩種情況,我們可以得到一條核心偽**:

p[i] = min(p[i'],r - i + 1);//前面是代表的被完全包含的情況,後面則是沒有被完全包含的情況,兩種情況因為是求可**況,所以是求min
\(i'\)實際**是寫成\(2 * pos - i\)

這沒辦法,前面的資訊已經用不上了,暴力修改,同時更新\(r\)以及 \(pos\),此時初始化\(p[i]等於1\)

核心偽**:

while(str[i + p[i]] == str[i - p[i]])p[i] ++;

// 這裡就是暴力擴充套件,然後前面的伏筆邊界處理就在此用上了,因為邊界字元不同,所以我們不會把邊界算進去

然後就演算法至此完結撒花了,給出演算法核心**:

int manacher()

maxlen = max(maxlen,p[i] - 1);//更新maxlen

} return maxlen;

}

嚴謹時間複雜度證明: 翻到最下面

最後還是貼一下模板完整**(luogu上的,居然是藍題......)

(主要是填補特殊符號那一段**):

#include using namespace std;

char a[31000005];

char str[31000005];

int p[31000005];

int lena,lenstr;

void manacher()

maxlen = max(maxlen,p[i] - 1);//更新maxlen

} cout << maxlen;

}int main()

lenstr = 2 * lena + 1;

str[lenstr + 1] = '$';

//實際上我們用到的是1 到 lena*2+1 這段區間的字元,最前面和最後面的兩個是為了處理邊界

manacher();

return 0;

}

學習筆記 Manacher演算法

manacher 中文 馬拉車 演算法,即求解給定字串中最長回文子串長度的演算法。洛谷p3805 給出乙個長度為 n 的只由小寫英文本元 mathtt 組成的字串 s 求 s 中最長回文串的長度 一般情況下回文串有奇偶分類。為了避免分類,我們在字串中間新增特殊字元 不妨用 在串的首尾加上不同的特殊字...

學習筆記 manacher演算法

一.關於manacher manacher演算法用於求解乙個字串的回文子串半徑長度。它可以線性地求解對於字串中的每乙個字元,以它本身為中心的最長回文串的半徑。而且這個回文串的每乙個以這個字元為中心的子串都是回文串。這個演算法的時間複雜度為o n 二.回文子串長度的奇偶性帶來的問題。當乙個回文串長度是...

Manacher演算法 學習筆記

首先,強烈安利一篇文章,這篇文章對於 manacher 的講解本人感覺非常到位。傳送門相信大家都知道的乙個方法 列舉字串的每乙個位置作為回文子串的對稱中心,同時向左向右擴充套件,判斷是否相等,然後每次儲存之前求取的最大回文子串長度,時間複雜度為 o n 2 在列舉時,還需要考慮對奇數回文串和偶數回文...