暑假上的字串課沒聽懂幾個字,所以現在像在學新演算法一樣
好像就是在學新演算法,不管了
其實講真的字串的演算法都比較巧妙啊!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 在列舉時,還需要考慮對奇數回文串和偶數回文...