Manacher演算法 求最長回文串的利器

2021-09-06 18:29:59 字數 2858 閱讀 7393

求最長回文串的利器 - manacher演算法

manacher主要是用來求某個字串的最長回文子串.

不要被manacher這個名字嚇倒了,其實manacher演算法很簡單,也很容易理解,程式短,時間複雜度為o(n).

求最長回文子串這個問題,我聽說有個分治+拓展kmp的演算法,字尾陣列也可以.

但是對於求回文串來說,manacher演算法肯定有很多其他演算法沒有的優點。

現在進入正題:

首先,在字串s中,用rad[i]表示第i個字元的回文半徑,即rad[i]盡可能大,且滿足:

s[i-rad[i],i-1]=s[i+1,i+rad[i]]

很明顯,求出了所有的rad,就求出了所有的長度為奇數的回文子串.

至於偶數的怎麼求,最後再講.

假設現在求出了rad[1..i-1],現在要求後面的rad值,並且通過前面的操作,得知了當前字元i的rad值至少為j.

現在通過試圖擴大j來掃瞄,求出了rad[i].再假設現在有個指標k,從1迴圈到rad[i],試圖通過某些手段來求出[i+1,i+rad[i]]的rad值.

根據定義,黑色的部分是乙個回文子串,兩段紅色的區間全等.

因為之前已經求出了rad[i-k],所以直接用它.有3種情況:

如圖,rad[i-k]的範圍為青色.因為黑色的部分是回文的,且青色的部分超過了黑色的部分,所以rad[i+k]肯定至少為rad[i]-k,即橙色的部分.

那橙色以外的部分就不是了嗎?這是肯定的.因為如果橙色以外的部分也是回文的,那麼根據青色和紅色部分的關係,可以證明黑色部分再往外延伸一點也是乙個回文子串,這肯定不可能,因此rad[i+k]=rad[i]-k.為了方便下文,這裡的rad[i+k]=rad[i]-k=min(rad[i]-k,rad[i-k]).

如圖,rad[i-k]的範圍為青色.因為黑色的部分是回文的,且青色的部分在黑色的部分裡面,根據定義,很容易得出:rad[i+k]=rad[i-k].為了方便下文,這裡的rad[i+k]=rad[i-k]=min(rad[i]-k,rad[i-k]).

根據上面兩種情況,可以得出結論:當rad[i]-k!=rad[i-k]的時候,rad[i+k]=min(rad[i]-k,rad[i-k]).

注意:當rad[i]-k==rad[i-k]的時候,就不同了,這是第三種情況:

如圖,通過和第一種情況對比之後會發現,因為青色的部分沒有超出黑色的部分,所以即使橙色的部分全等,也無法像第一種情況一樣引出矛盾,因此橙色的部分是有可能全等的,但是,根據已知的資訊,我們不知道橙色的部分是多長,因此就把i指標移到i+k的位置,j=rad[i-k](因為它的rad值至少為rad[i-k]),等下次迴圈的時候再做了.

整個演算法就這樣.

至於時間複雜度為什麼是o(n),我已經證明了,但很難說清楚.所以自己體會吧.

上文還留有乙個問題,就是這樣只能算出奇數長度的回文子串,偶數的就不行.怎麼辦呢?有一種直接但比較笨的方法,就是做兩遍(因為兩個程式是差不多的,只是rad值的意義和一些下標變了而已).但是寫兩個差不多的程式是很痛苦的,而且容易錯.所以一種比較好的方法就是在原來的串中每兩個字元之間加入乙個特殊字元,再做.如:aabbaca,把它變成a#a#b#b#a#c#a,這樣的話,無論原來的回文子串長度是偶數還是奇數,現在都變成奇數了.

模板題:hdu 3068

/** this code is made by crazyacking

* verdict: accepted

* submission date: 2015-08-19-15.59

* time: 0ms

* memory: 137kb

*/#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define maxx 20000050

#define  ll long long

#define  ull unsigned long long

using

namespace

std;

char

str[2*

maxx

];char s

[maxx

];int p

[maxx

];void

manacher(

int*p ,

char

*str

,int

len) }

}int

main()

manacher(p

,str

,n);

intans=

1;

for(

inti=

0; i

i++)

ans=max(

ans,p [

i]);

printf(

"%d\n"

,ans

-1); }

return

0; }

求最長回文串 Manacher演算法

o n 效率的字串求最長回文串,感覺這個blog上寫的很詳細 有幾個要點 1 先要將字串擴充套件成2 l 1長度的,在每兩個字元之間要加上乙個用不到的字元,比如 方便處理偶數回文串。2 在擴充套件後的字串兩端要加一些特殊字元,防越界。3 ra i 陣列表示以i為中點的最長回文串,mx表示以j 1 j...

manacher演算法求最長回文串長度

manacher演算法 定義陣列p i 表示以i為中心的 包含i這個字元 回文串半徑長 將字串s從前掃到後for int i 0 i由於s是從前掃到後的,所以需要計算p i 時一定已經計算好了p 1 p i 1 假設現在掃瞄到了i k這個位置,現在需要計算p i k 定義maxlen是i k位置前所...

manacher演算法 O n 求最長回文子串

樸素的做法是求出以每個字元為中心的回文串長度,複雜度為 而manacher演算法可以在o n 時間內求解,奇數長度和偶數長度可以統一處理。根據回文串的對稱性,避免了大量不必要的比較。處理技巧 相鄰的字元之間插入乙個分隔符,串的首尾也要加,以 為例,則長度為n的字串經過處理之後變成2n 1奇數長度的字...