1. 程式人生 > 其它 >學習筆記——manacher演算法

學習筆記——manacher演算法

前言

$manacher$演算法,這個被$OIer$戲稱為馬拉車的演算法,作為字串入門演算法,非常值得$OIer$學習,並且學會其核心思想--不斷利用之前以求的值來更新之後待求的值。掌握好它,我們就可以開啟$OI$字串演算法的大門。

一、manacher演算法的目的(解決神馬問題)

$manacher$演算法是用於求最長迴文子串長度的演算法。

什麼迴文串?顧名思義,迴文串就是一個字串順序讀和倒序讀都一樣。

舉個栗子:$aba$ $aaaaaa$ $IOI$

二、manacher演算法的基本思路

首先,我們來看到問題:給定一個字串,求其最長迴文子串長度。

$1$.我們來看一看最暴力的解法:列舉所有子串,再分別判斷該串是否是迴文串。時間複雜度$O(n^3)$。暴力不愧是暴力,這時間複雜度直接上天

$2$.既然暴力不行,那我們稍微優化一下。我們可以列舉每一個點作為對稱點(就是該回文子串的對稱中心),再暴力掃描求出最長迴文子串長度。時間複雜度$O(n^2)$。

$3$.改進後還是有點慢,我們可不可以再快一點?這是,我們今天的主角--manacher演算法就閃亮登場了。

我們可以沿著$2$的思路,列舉對稱點,並令$p[i]$為該對稱點所能延伸的最長迴文半徑(從對稱點到該回文子串的左端點或右端點(包含端點和對稱點))。

這時,我們遇到了第一個問題--如果是偶迴文串,那我們該怎麼辦?

我們可以在所有字元之間插入一個無關字元,讓偶串變成奇串。

舉個栗子:aba->@#a#b#a#!  (前後兩個端點插入不同符號防越界)
		  abba->@#a#b#b#a#! 
            
這樣,我們就可以愉快的列舉對稱點了。  

之後,我們可以得到一個顯然才怪的結論--迴文串長度==$p[i]-1$。

為了防止我們看的雲裡霧裡,還是補充一個解釋比較好:

以abba為例,處理後變成@#a#b#b#a#! 那麼最中間的#號的$p[i]$值為$5$,$5-1=4$,從數值上看,它們是相等的。

那有沒有什麼證明呢?當然要有,不然怎麼用這個演算法。

新串相當於在原串的基礎上$\times 2+1$,我們取它的迴文半徑就可以抵消掉$\times 2$的效果,再$-1$就可以得到真正的迴文串長度。

說了這麼多,提升效率的呢?

我們定義$mx$表示當前找到的最長迴文子串的最右端點的下標,$id$表示$mx$的對稱中心。用$i$表示當前要匹配的節點。

1.當$i<mx$時

利用迴文串的性質,我們可以得到$i$關於$id$對稱的點$j=(id<<1)-i$。

(1)當$p[j]<j$到$id$左端點的距離,如下圖

顯然,$p[i]=p[j]$,且$p[i]$不可以再向兩邊擴充套件。

(2)當$p[j]==j$到$id$左端點的距離,如下圖

顯然,$p[i]=p[j]$,且$p[i]$可以再向兩邊擴充套件。

(3)當$p[j]>j$到$id$左端點的距離,如下圖

此時$p[i]=mx-i$,且$p[i]$不可以再向兩邊擴充套件。

因為如果可以擴充套件,則$cd$,又因為$ab$且$bc$,所以$ad$,那麼$mx$就可以再拓展一位,產生矛盾,捨去。

2.當$i>mx$時

直接$p[i]=1$,之後再判斷是否可以拓展。(因為之前推出的$p[i]$值無法更新當前要求的值)

好了,直接上核心程式碼

	for(R int i=1;i<len;++i) {//len是新陣列長度
		if(i<mx) p[i]=min(p[(id<<1)-i],mx-i);//同1
		else p[i]=1;//同2
		while(str[i+p[i]]==str[i-p[i]]) ++p[i];//判斷是否可以再向兩邊拓展
		if(p[i]+i>mx) {mx=i+p[i];id=i;}//更新
		ans=max(ans,p[i]-1);//更新答案
	}

參考資料:

Chrety大佬的blog

老規矩,練手題

1.板子(題解)(我的程式碼)

2.求前$k$長的所有迴文子串長度的乘積(題解)(我的程式碼)

3.最長雙迴文串(題解)(我的程式碼)