我們先看兩個字串:
abccba
abcdcba
顯然這兩字串是回文的
然而兩個串的對稱中心的特性不同,第乙個串,它的對稱中心在兩個c中間,然而第二個串,它的對稱中心就是d。這樣我們如果要記錄回文串的對稱中心,就顯得複雜了。
為了解決這個問題,把兩種情況統一起來,我們就在字母之間插入隔板,這樣兩個問題就統一了,因為所有的對稱中心都會有個字元與之對應。像這樣
~a|b|c|c|b|a|
(注意到我們這裡還插入了乙個「~」,原因我們待會說明)
manacher為什麼有如此優秀的複雜度呢?讓我用比還要清楚的文字說明一下
因此,為利用特性(2),我們若記錄每個字元作為回文對稱中心時的回文串的半徑,就可以知道該字元,它,關於某另乙個對稱中心(稱此對稱中心對應的回文串叫做「大回文串「)對稱的,在大的回文半徑內,對應字元的部分回文半徑了。
記錄這些資料到p陣列。同時記錄乙個mid,乙個r,分別代表已經確定的右側最靠右的回文串的對稱中心和右邊界
然而,考慮一些情況,發現我們只能確認我們已知的回文串內的對稱關係和回文半徑等量關係,關於這個大回文串右側的世界,我們一無所知。
那麼,當我們掃瞄到乙個新的字元時,怎麼先確定它的部分回文半徑呢?
若當前掃瞄到的位置為i,若mid<=i<=r,則我們可以找到它的乙個對稱點。這個點的位置是多少?是mid×2-i。我們現在對其推導一下。
設:這個新點位置為i,它關於mid對稱的點為j,將整個字串看做以下標0位置為原點的數軸,我們由中點公式可得:
證畢。就是這樣小學生都會的推導過程,幾乎每篇題解都是 直接擺出結論 ,不給出證明和推導,要求學習者自己」找規律」 , 我真是不敢苟同。希望大家以後發題解要嚴謹一點,至少自己沒有真正理解就不要發題解,讓人覺得manacher是難以學習的演算法。至少當我在學習的時候,是一頭霧水。
所以,拓展乙個新點時,我們不必從這個點左右兩邊第乙個位置開始向兩邊拓展,可以預先確定一部分回文串。就是因為這個,manacher的複雜度是線性的。
故此,要取乙個min 這裡給出**:
p[i]=min(p[mid*2-i],r-i)
最終答案是
p(max)-1
講到這裡,應該十分清楚了。
#include#include#include#includeusing namespace std;
const int maxn=11000002;
char data[maxn<<1];
int p[maxn<<1];
int cnt=1;
#define rp(t,a,b) for(int t=(a),edd=(b);t<=edd;t++)
inline void qr()
return;
}int maxans,rb,mid;
inline int cmpless(int x,int y)
cout《祝大家學習愉快
洛谷P3805 模板 manacher演算法
給出乙個只由小寫英文本元 texttt a,texttt b,texttt c,ldots texttt y,texttt z 組成的字串 s 求 s 中最長回文串的長度 字串長度為 n 先將每兩個字母之間插入乙個奇怪的符號。記 p x 表示第 x 個字元為對稱中心,能擴充套件的最遠距離是多少。記 ...
題解 P3385 模板 負環
這題有毒!spfa判負環常用的有兩種,一種是判斷鬆弛次數,但它會繞環好多次,另一種是判斷最短路徑的長度,只要繞環一次,前一種本題過不了。判斷最短路徑的長度的意思就是用乙個len陣列記錄從源點到當前節點的最短路徑經過的邊數,並在鬆弛時令len v len u 1。若len v n則必然存在負環。為了解...
堆 題解 P3378 模板 堆
堆就是一顆二叉樹,滿足父親節點總是比兒子節點大 小 因此,堆也分為大根堆和小根堆,大根堆就是父親節點比兒子節點大,小根堆正好相反。注意加粗的地方,是每乙個節點哦!還是直接看例題吧,這樣講起來更加生動。上題 模板 堆 解析 這道題明顯就是乙個小根堆,那,怎麼實現呢?熱愛陣列的我選擇了陣列實現明明就是指...