1. 程式人生 > 實用技巧 >[學習筆記]馬拉車-Manacher

[學習筆記]馬拉車-Manacher

[學習筆記]馬拉車-Manacher

一.概念

​ 在日常做題中,經常會遇到求迴文串的問題,而馬拉車演算法可以在 \(O(n)\) 的時間中求出一個字串的最大回文子串。順帶一提,這個演算法是音譯。然後掛一下模板連結

二.基本思想

​ 首先對於以下兩個串 \(aabaa\)\(aabbaa\) 很顯然它們整個都是迴文的,但是如果要尋找一個所謂的“中心”,第一個串好找,第二個則說不清。然後馬拉車的一個奇妙策略是插空,即在字母與字母間插入一個“隔板”,這裡我用的'|',於是兩個串就變為了\(|a|a|b|a|a|\)\(|a|a|b|b|a|a|\),

​ 把插進去的隔板計算在內的話,則第一個串的”中心“是'b',第二個串的”中心“是'|',這種策略使串都成了奇數長度,於是方便找中心。

​ 此時引入兩個變數,\(p[i]\)表示以i為中心的最大回文串的半徑,\(mid,mx\)則分別表示當前已知的最長迴文串的中心與右端點。,那麼對於一個掃描到的新點 i 。有多種情況。

​ case 1:它在已知的右端點內。那麼相關於中心,(有點平面鏡成像的感覺?也有可能是數軸?),i 有個對稱點 j,由對稱點公式易得(初一數學奧),\(j=2*mid-i\).那麼 j 有 一個已知的 p[j],由於\(mid*2-mx——mx\)是迴文串,理想情況下可以令 p[i]=p[j] 但是實際上有可能i+p[i] 延伸到了 mx 的右邊,此時無法根據 mx 的迴文串性質直接相等,那麼限制一下使 \(p[i]=min(p[(mid<<1)-i],mx-i+1)\)

就好。當然這還沒完,需要手動探測一下兩邊(看程式碼就很清楚)

​ case 2:不在右端點內,直接手動探測奧。

最後注意維護一下mx與mid就好。由於mx一直是一直增加的,i也是由1~n,所以是\(O(n)\)

三.CODE(模板)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<fstream>
using namespace std;
const int MAXN=1.1*1e7;
char ch[MAXN*2];
int mid,mx,tot,p[2*MAXN],ans;
void read(){
	char c=getchar();
	ch[0]='~';ch[(tot=1)]='|';
	while(c<'a'||c>'z')c=getchar();
	while('a'<=c&&c<='z'){
		ch[++tot]=c,ch[++tot]='|';
		c=getchar();
	}
}
int main(){
	read();
	for(int i=1;i<=tot;++i){
		if(i<=mx)p[i]=min(p[(mid<<1)-i],mx-i+1);
		while(ch[i-p[i]]==ch[i+p[i]])++p[i];
		if(i+p[i]>mx)mx=i+p[i]-1,mid=i;
		if(p[i]>ans)ans=p[i];
	}
	cout<<ans-1;
	return 0;
}