manacher 演算法流程
為了不判斷奇偶,方便比較,在原字串裡新增一些特殊字元
明確幾個變數,\(r\)代表我遍歷過的位置中回文串能延伸的最遠位置,\(mid\)是\(r\)對應的中心位置,那麼\(mid\)對應的左端點就是\(2mid-r\),\(f_i\)表示\(i\)為中心的回文半徑長度
接下來就分以下情況討論,可以自己畫圖理解(我並不會畫圖)
我當前位置\(i,我一定能在包含他的最長回文串的對稱部分找到乙個在最長回文串包含範圍內和他相同的回文串(因為字母本身也是乙個回文串,這樣我就統一說成回文串了),但是我們並不能保證在最長回文串外部他們倆還相等,所以我要在\(f_\)和\(r-i+1\)之間取較小的乙個
承接我上面的\(1\),因為我只在最長回文串內部判斷了它的回文串,回文串外部的還沒有判斷,所以我向兩邊擴充套件
在我判斷的過程中不斷更新\(mid\)和\(r\)
code:
#include #include #include using namespace std;
int read()
while (ch >= '0'&&ch <= '9')
return x*a;
}const int maxn = 5e7+10;
char a[maxn];
int cnt;
void init()
int f[maxn],ans;
int main()
printf("%d\n",ans-1);
return 0;
}
例題:
相交的回文串對數,我們可以轉化為總回文串對數-不相交的回文串對數,兩個回文串不相交,那麼他們中間一定有至少乙個斷點,列舉斷點,恰好使他是被分割開的後面的回文串的開頭(這樣才可以保證不被重複計算),再看在他左邊有多少個回文串的結尾乘法原理做就好了。
當我們有乙個半徑為\(r\)的回文串那麼我能得到的回文串個數也是\(r\),馬拉車可以處理出以每個字元為中心的回文串半徑,但是注意我們新增了字元,所以我的半徑其實就是直徑+1,碰巧我的半徑需要向上取證,所以我的回文串總數就是\(ans=\sum\limits_^ \dfrac\),我總回文串的對數就是\(ans=\dfrac\)
考慮我們如何求區間內回文串結尾的個數和乙個點的回文串開頭的個數,就像上面說的,當我有乙個半徑為\(r\)的回文串,其實它的左半徑上的所有點都可以作為乙個回文串的開頭,右半徑的所有點都可以作為乙個回文串的結尾,其實這就是乙個區間加的操作,二次差分就可以求出這個數量。
code:
#include #include #include #define int long long
using namespace std;
int read()
while (ch >= '0'&&ch <= '9')
return x*a;
}const int maxn = 4e6+10,mod = 51123987;
char a[maxn];
int cnt,n;
void init()
int f[maxn],ans;
int l[maxn],r1[maxn];
signed main()
for (int i = 1;i <= cnt;i++) l[i-f[i]+1]++,l[i+1]--,r1[i+f[i]]--,r1[i]++;
for (int i = 1;i <= cnt;i++) (ans += f[i]/2)%=mod;
ans = (ans*(ans-1)/2)%mod;
for (int i = 1,s = 0;i <= cnt;i++)
printf("%lld\n",(ans%mod+mod)%mod);
return 0;
}
明確幾個變數:\(r_i\)表示以\(i\)結尾的最長回文串的長度,\(l_i\)表示以\(i\)開頭的最長回文串個數。
轉移方程應該還是挺好想的,當我馬拉車求出所有的半徑之後:\(l_=max(f_i+1,l_)\),\(r_=max(f_i+1,r_)\)
但是直到上面我們只更新了兩邊的最長回文串長度,中間的最長回文串長度我怎麼更新呢???
列舉斷點'|',因為我們在原串中間新增了字元,所以每挪動一次斷點回文串長度會-2,得到轉移方程:
\(r_i=max(r_i,r_-2)\),\(l_i=max(l_i,l_-2)\)
最長雙回文串長度就等於從同一點開始和結束的兩個回文串長度相加,注意我這同一點一定是特殊字元斷點
code:
#include #include #include #define int long long
using namespace std;
int read()
while (ch >= '0'&&ch <= '9')
return x*a;
}const int maxn = 4e6+10,mod = 51123987;
char a[maxn];
int cnt,n;
void init()
int f[maxn],ans;
int l[maxn],r1[maxn];
signed main()
for (int i = 1;i <= cnt;i++)
for (int i = 1;i <= cnt;i+=2)
printf("%d\n",ans);
return 0;
}
最長回文串(馬拉車演算法)
最長回文子串 manacher演算法 馬拉車演算法 馬拉車演算法需要計算以每個字元為中心的回文串半徑。並記錄最右邊界 馬拉車演算法基於這樣乙個事實,從回文串的中心到兩邊是對稱的,意味著以兩邊對稱的字元為中心的回文串半徑相等 在不超過最右邊界的情況下,如果超出就需要擴充套件搜尋 public stri...
最長回文串 馬拉車演算法
有兩個長度均為n的字串a和b。可以從a中選乙個可以為空的子串a l1 r1 b中選乙個可以為空的子串b l2 r2 滿足r1 l2,然後把它們拼起來 a l1 r1 b l2 r2 求用這樣的方法能得到的最長回文串的長度。注意 求的不是本質不同的回文串個數哦!解題報告 找兩個之間的最長回文串,只不過...
最長回文字串 馬拉車演算法
很簡單的例題,就比如hdu3068那個,模版題。首先我們可以考慮暴力,然後可以列舉中心,當你列舉中心的時候,先是1,再是2,如果2不行,那就可以退出了,因為以該點為中心的字串不能繼續拓展了。我們應該開始列舉下乙個中心點了。然後我們考慮優化,馬拉車演算法的核心就是利用了前面計算的資訊,從而高效的得出最...