Manacher入门
寫在前面
manachermanachermanacher比想象中好理解得多
至少它給了我學(xué)習(xí)字符串的信心
能干啥
manachermanachermanacher,中文馬拉車(您別說,這名字還挺形象),主要用于計(jì)算字符串每一個(gè)位置為對(duì)稱中心的回文串長(zhǎng)度(等價(jià)于個(gè)數(shù))
包括偶回文,即長(zhǎng)度為偶數(shù)的回文串
算法流程
考慮下面的問題:給一個(gè)字符串,求最長(zhǎng)回文子串
我會(huì)暴力!
枚舉判斷O(n3)O(n^3)O(n3)
我會(huì)優(yōu)化!
考慮到以ccc為中心,acbacbacb不是回文串,左右怎么接都不是回文串
所以可以枚舉中間點(diǎn)(偶回文判斷一下即可),然后向兩邊拓展
復(fù)雜度O(n2)O(n^2)O(n2)
我會(huì)玄學(xué)!
O(n2)O(n^2)O(n2)還不夠 不符合字符串算法均為線性復(fù)雜度公理
在此基礎(chǔ)上繼續(xù)優(yōu)化
我們記錄maxrmaxrmaxr為目前找到的回文串右端點(diǎn)的最右的位置 pospospos為這個(gè)回文串的對(duì)稱中心
開一個(gè)數(shù)組pip_ipi?表示回文半徑(和其他文章不同,本文回文半徑指一個(gè)端點(diǎn)到中心的距離,即直接相減)
回文串最本質(zhì)的特征是什么?左右關(guān)于中心對(duì)稱。
如果一個(gè)回文串對(duì)稱中心的一側(cè)有另一個(gè)回文子串,那么對(duì)稱過去也有一個(gè)相同長(zhǎng)度的回文子串
黃色部分完全一致,這就是manacher的核心
(先不考慮偶回文)
① i<maxri<maxri<maxr(取不取等于無所謂)
設(shè)j,ij,ij,i關(guān)于pospospos對(duì)稱 即j=pos?2?ij=pos*2-ij=pos?2?i
根據(jù)上面的推導(dǎo),令pi=pjp_i=p_jpi?=pj?即可
當(dāng)然如果jjj為中心的最長(zhǎng)回文左端點(diǎn)越界了
那就不能保證上面的性質(zhì)(實(shí)際上可以保證不成立),所以要和j?maxlj-maxlj?maxl(即maxr?imaxr-imaxr?i)取minminmin。
jjj越界時(shí)還需要讓iii繼續(xù)拓展。當(dāng)然為了刷短代碼減少討論,都拓展一下好了
②i≥maxri \geq maxri≥maxr
啥都不能保證,直接暴力擴(kuò)展
綜上:
然后……對(duì),沒了,就三行。
這就是manachermanachermanacher的全過程,復(fù)雜度O(n)O(n)O(n),后面有證明
實(shí)現(xiàn)
上面沒有討論偶回文,主要是轉(zhuǎn)化過程比較難看,容易使人喪失信心。
其實(shí)也不難。只需要在字符兩兩之間加一個(gè)字符,如‘#’;然后首尾也加上‘#’,再在開頭加一個(gè)標(biāo)識(shí)符防止越界。注意要和之前的不同,如‘@’
這樣偶回文可以看做中心是‘#’的回文
普及T1模擬水平
這樣處理后,所有回文串左右端點(diǎn)都是‘#’,然后就可以跑了
證明
什么?不是只改了一個(gè)地方嗎?怎么變O(n)O(n)O(n)了?
回顧整個(gè)流程,真正暴力拓展的時(shí)候要么jjj左端點(diǎn)越界,要么iii越界(jjj左端點(diǎn)沒越界時(shí),根據(jù)反證法,iii實(shí)際上無法拓展)
jjj左端點(diǎn)越界時(shí),iii右端點(diǎn)出生點(diǎn)就在maxrmaxrmaxr,再拓展就超出去了
也就是說,每次暴力拓展,都意味著maxrmaxrmaxr更新,而maxrmaxrmaxr只會(huì)往右跑
換一個(gè)角度,實(shí)際上暴力拓展是把maxrmaxrmaxr推到最右邊,次數(shù)是O(n)O(n)O(n)的
所以總復(fù)雜度是O(n)O(n)O(n)的
代碼
#include <iostream> #include <cstdio> #include <cstring> #define MAXN 1000005 using namespace std; char s[MAXN],ss[MAXN]; int pos,maxr; int p[MAXN]; int main() {scanf("%s",s+1);int n=strlen(s+1);for (int i=1;i<=n;i++) ss[i<<1]=s[i],ss[(i<<1)|1]='#';n=(n<<1)|1,ss[0]='@',ss[1]='#';strcpy(s,ss);for (int i=1;i<=n;i++){if (i<maxr) p[i]=min(p[(pos<<1)-i],maxr-i);while (s[i-p[i]-1]==s[i+p[i]+1]) ++p[i];if (i+p[i]>maxr) maxr=i+p[i],pos=i;}return 0; }由于所有位置為中心的最長(zhǎng)回文串左右端點(diǎn)都是‘#’
回文串長(zhǎng)度leni=2pi+1?12=pilen_i= \frac {2p_i+1-1}{2} = p_ileni?=22pi?+1?1?=pi?
然后用來搞事情就可以了
總結(jié)
以上是生活随笔為你收集整理的Manacher入门的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 头部会产生很小的颤动声音是怎么回事?
- 下一篇: 中高级数论 [欧拉函数线性筛,二次剩余]