Manacher處理字符串

Manacher算法在求迴文串的時候是一種很高效的算法,這裏講解一些這個算法的原理以及簡單的模板。

  1. 原理講解:

    • 首先對字符串進行預處理,在字符串的中間以及兩邊都加上一個不會出現的字符,然後這樣就可以保證我們求的字符串都是一個長度爲奇數的迴文串。如下:
      原始字符串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].
  2. 代碼實現

下面貼上一份代碼:

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章