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爲到目前爲止的迴文串的最右邊界,即目前爲止的所
有迴文串中,最右的字符下標最大的那個下標;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; }
完結撒花