manacher(馬拉車算法)

Manacher(馬拉車算法)


序言

mannacher 是一種在 O(n)時間內求出最長迴文串的算法ios

咱們用暴力求解最長迴文串長度的時間複雜度爲O(n3)c++

很明顯,這個時間複雜度咱們接受不了,這時候,manacher也就是俗稱的馬拉車算法就出世了算法

算法描述

先考慮一種在O(n2)的時間複雜度內求解的算法數組

咱們能夠從左到右枚舉字符串的每個字符,以當前字符爲起點,向左,和向右同時延伸來求解優化

迴文長度,但咱們深刻分析一下,發現,這個算法明顯是有漏洞的,它只能解決字符串長度爲spa

爲奇數的迴文長度,偶數的字符串沒法由它求出迴文長度。code

好比aba用該算法求出的值爲3是正確的,但abba用該算法求出的值倒是1,很明顯,是錯誤的,那麼咱們考慮,ci

如何去優化該算法,使得奇數和偶數都能解決,咱們考慮在字符串的首尾和字符字符串

串間隔中插入一個特殊字符如 #,例如abba,插入後就變爲了#a#b#b#a#字符串的長get

度由n變爲了2n+1這樣就能夠保證字符串的長度爲奇數了,證實很簡單,2n定爲偶數

則2n+1必定爲奇數而後即可以經過上述算法求出目前的迴文長度了。

但,n2 的算法仍然沒法知足咱們的需求,咱們考慮繼續優化,那麼如何優化呢?

咱們須要引入幾個定義

下述定義用未插入特殊字符的字符串進行解釋

1:迴文中心,即一回文串的中心字符好比abcba這個迴文串,從左向右數的第3個字符即c

便爲其迴文中心咱們由於咱們對字符串進行了處理,因此便保證了每一串都有其迴文中心

2,迴文半徑,即迴文串的最右邊界到迴文中心的字符個數(包含迴文中心)

咱們能夠發現,每一個串的迴文半徑的長度*2-1即是所求迴文串的長度

因此,咱們只要求出那個最大的迴文半徑即可獲得最長迴文串長度

那麼,咱們如何求解呢?

manacher算法的本質即是對上面所提的n2的優化

咱們看下圖,假設咱們目前枚舉到i,定義r爲到目前爲止的迴文串的最右邊界,即目前爲止的所

無標題.png

有迴文串中,最右的字符下標最大的那個下標;mid則爲 r 所在迴文串的迴文中心

咱們對i進行討論

當i<r時由於i位於以mid爲迴文中心的迴文串中,因此以i爲迴文中心的字符串有可能被以mid

爲迴文中心的字符串包含,假設被包含,咱們利用迴文串的對稱性可知,以i關於mid的對稱點也就是j點

爲迴文中心的字符半徑等於以i爲半徑的迴文半徑

咱們直接賦值便可

定義p數組爲迴文半徑長度

那麼咱們如何判斷呢?當r-i的長度要大於p [j] 時,p[j]必定沒有超過mid的範圍,那麼,咱們直接對稱過去更新p[i]便可

反之,當r-i>r-i 代表p[j]有可能包含mid範圍以外的數,咱們沒法將p[j]賦給p[i]便在範圍內將r-i賦給p[i];

本質融合成一句代碼即是

p[i]=p[mid*2-i]>mir-i?mir-i:p[mid*2-i];

咱們求j的下標利用中點座標公式求解便可

貼一發代碼

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn =2e7+10;
char s[maxn];
char ns[maxn];
int p[maxn];
int getle(){
	int len=strlen(s);
	int j=2;
	ns[0]='~';//設置枚舉邊界,字符串右側帶有換行,因此咱們右側沒必要放字符了
	ns[1]='$';
	for(int i=0;i<len;i++){
		ns[j++]=s[i];
		ns[j++]='$';
	}
	return j;
}
int manacher(){
	int len=getle();
	int mid=1;
	int mir=1;
	int ans=-1;
	for(int i=1;i<len;i++){
		if(mir>=i){
			p[i]=p[mid*2-i]>mir-i?mir-i:p[mid*2-i];
		}
		else{
			p[i]=1;
		}
		while(ns[i+p[i]]==ns[i-p[i]]){
			p[i]++;
		}
		if(p[i]+i>mir){
			mid=i;
			mir=p[i]+i;
		}
		ans=max(p[i]-1,ans);
	}
	return ans;
}
int main(){
	cin>>s;
	cout<<manacher();
	return 0;
}

完結撒花

相關文章
相關標籤/搜索