Manacher算法在求迴文串的時候是一種很高效的算法,這裏講解一些這個算法的原理以及簡單的模板。
-
原理講解:
- 首先對字符串進行預處理,在字符串的中間以及兩邊都加上一個不會出現的字符,然後這樣就可以保證我們求的字符串都是一個長度爲奇數的迴文串。如下:
原始字符串s=“absbffbsma",插入一個字符’#'後形成的新字符串sn="#a#b#s#b#f#f#b#s#m#a#". 對與這樣的處理,我們可以發現這個字符串有兩個性質:
1.迴文串的長度總是一個奇數的
2.迴文串的兩端一定是自己所插入的那個字符串。 - 性質利用:
第一個性質:首先說明一下如何求新形成的字符串sn的迴文串,我們創建一個數組p[inf],對應的是每一個位置i時,由位置i形成的迴文串能向一邊延伸的最大長度。另外創鍵兩個整數pos,R,他們所代表的是目前位置前面的迴文串中右端點最大的位置R和該回文串中心的位置pos
求解p[i],在前面i-1的迴文串都已經求得的情況下位置i的值一個是i關於位置pos對稱位置j的值p[i],另一種是i的最右端超出了R的右側,此時暴力求出R右側的長度。
由此我們可以看出,這個算法的時間複雜度是線性的,因此是一種非常高效的算法。
第二個性質:第二個性質主要是用來字符串sn的答案轉換成原來字符串s的。在sn中,以i爲中心的迴文串長度爲2×p[i]-1 (p[i]最小值1),由於已經保證的兩端的字符串一定是我們插入的字符串,所以其中原來的字符串和插入的字符串個數分別爲p[i]-1和p[i].
- 首先對字符串進行預處理,在字符串的中間以及兩邊都加上一個不會出現的字符,然後這樣就可以保證我們求的字符串都是一個長度爲奇數的迴文串。如下:
-
代碼實現
下面貼上一份代碼:
#include<iostream>
#include<string>
using namespace std;
const int inf = 1e4;
string s;
char sn[inf];
int p[inf];
int main()
{
while(cin>>s){
if(s==".")break;
for(int i=1;i<=s.length();i++){sn[i*2-1]='$',sn[i*2]=s[i-1];}
sn[s.length()*2+1]='$';
int pos=0,R=0;
int len=s.length()*2+1;
for(int i=1;i<=len;i++){
if(i<R)p[i]=min(p[pos*2-i],R-i);else p[i]=1;
while(i+p[i]<=len&&i-p[i]>=1&&sn[i+p[i]]==sn[i-p[i]])p[i]++;
if(i+p[i]>R){
pos=i;R=i+p[i];
}
}
int ans=0;pos=0;
for(int i=1;i<=len;i++)if(ans<p[i]){ans=p[i];pos=i;}
//單純求出最大回文串的長度
cout<<ans-1<<endl;
//下面幾行是輸出原來字符串中的迴文串
// for(int i=pos-p[pos]+1;i<pos+p[pos];i++){
// if(i%2==0)cout<<s[i/2-1];
// }
// cout<<endl;
}
return 0;
}