gdoi市選出了一道關於manacher的題目,於是打算學一下,但因為網路流鴿了一陣,最近才學完,學完後表示複雜度有點迷。manacher,俗稱馬拉車,是由一位名叫manacher的人與2023年提出的,這個演算法讓求最長回文子串的複雜度從o(n^2)下降到了o(n)。
先從n方演算法說起,n方演算法是每一位都向兩邊擴充套件,直到不回文。這個演算法只是對當前位置進行操作,而manacher演算法將前面算出的值進行記錄,用於後續的處理,從而降低複雜度。
我們平時用n^2演算法處理的時候就會考慮到乙個問題,就是乙個回文串的長度,如果是像aaa一樣的長度為奇數的串,它的中心是乙個字元;如果是像abba一樣長度為偶數的串,它的中心在兩個字元中間,這樣比較難處理。所以manacher演算法會在乙個字串每兩個字元中間以及字串頭尾插入『#』(也可以是其他的,有沒有出現在原字串中其實無所謂)。
如:abcdcba會變為#a#b#c#d#c#b#a#。
這裡我們引入乙個陣列p,p[i]表示以i為中心的最長回文串的半徑長度。我們用**舉個例子:s#
a#b#
a#b#
a#b#
a#p1
2141
6181
6141
21而更神奇的是以s[i]為中心的回文串長度就是p[i]-1。
我們需要記錄的除了p陣列,還有在現在已知(即之前處理過的)的所有回文串中右邊界最右的那個回文串c的中心位置mid以及其長度len(記錄其右邊界也可)。
設我們即將處理的位置為i,i關於mid的對稱點為j,那麼:
如果i > mid+len-1
即i在回文串c右邊界的右邊,沒有任何先前的資料可以利用,所以p[i]=1,直接暴力求p[i]。
如果j-p[j]+1 >= mid-len+1,
即以j為中心的最長回文串(下圖紅色部分)在回文串c內,則以i為中心的最長回文串(下圖藍色部分)一定也在該字串內,那麼就有p[i]=p[j],如下圖。
如果j-p[j]+1 < mid-len+1,
即以j為中心的最長回文串(下圖紅色部分)有一部分在回文串c外,則以i為中心的最長回文串(下圖藍色部分)至少有一部分可以確定(下圖綠色部分),那麼我們可以直接從確定的部分拓展算出p[i],這裡p[i]一定小於p[j]。
上面是乙個理解演算法的過程,事實上並不用如此複雜的操作。另外有乙個小技巧,可以在字串最前端插入乙個』$』,在字串最末端插入乙個』\0』,並以此來完成對回文串邊界的判斷,所以插入的可以不是上述的兩個字元,但是一定不能出現在原字串中。
那麼,上**:
#include
#include
#include
using
namespace
std;
const
int maxn=11000005;
char c[maxn],s[maxn*2+1];
int lenc,lens;
int p[maxn*2+1],mid,len,maxlen;
int minn(int a,int b)
int maxx(int a,int b)
void init()
s[lens++]='\0';
memset(p,0,sizeof(p));
}int main()
//雖然看上去會讓演算法退化但是演算法優啊
if (i+p[i]>mid+len)
//更新mid和len
maxlen=maxx(maxlen,p[i]-1);
}printf("%d",maxlen);
return (0-0);
}
manacher演算法 O n 求最長回文子串
樸素的做法是求出以每個字元為中心的回文串長度,複雜度為 而manacher演算法可以在o n 時間內求解,奇數長度和偶數長度可以統一處理。根據回文串的對稱性,避免了大量不必要的比較。處理技巧 相鄰的字元之間插入乙個分隔符,串的首尾也要加,以 為例,則長度為n的字串經過處理之後變成2n 1奇數長度的字...
求最長回文子串 Manacher 演算法
給定乙個字串,求出其最長回文子串。例如 1 s abcd 最長回文長度為 1 2 s ababa 最長回文長度為 5 3 s abccb 最長回文長度為 4,即 bccb。以上問題的傳統思路大概是,遍歷每乙個字元,以該字元為中點向兩邊查詢。其時間複雜度為 o n2 很不高效。1975 年,乙個叫 m...
求最長回文子串 ManaCher演算法
最長回文子串 求任一既定字串中,回文子串的最長長度 這是最容易想到的辦法。先遍歷獲得字串的所有子串,再對每個子串判斷其是不是回文串。對於長度為n的字串,子串個數為n n 1 2,加上對每個子串進行判斷,這種解法的時間複雜度為o n 3 就不寫了,下面重點介紹manacher演算法 由回文的定義可以推...