1. 程式人生 > >Manacher演算法詳解

Manacher演算法詳解

Manacher演算法

演算法總結第三彈 manacher演算法,前面講了兩個字串相演算法——kmp和拓展kmp,這次來還是來總結一個字串演算法,manacher演算法,我習慣叫他 “馬拉車”演算法。

相對於前面介紹的兩個演算法,Manacher演算法的應用範圍要狹窄得多,但是它的思想和拓展kmp演算法有很多共通支出,所以在這裡介紹一下。Manacher演算法是查詢一個字串的最長迴文子串的線性演算法。

在介紹演算法之前,首先介紹一下什麼是迴文串,所謂迴文串,簡單來說就是正著讀和反著讀都是一樣的字串,比如abba,noon等等,一個字串的最長迴文子串即為這個字串的子串中,是迴文串的最長的那個。

計算字串的最長迴文字串最簡單的演算法就是列舉該字串的每一個子串,並且判斷這個子串是否為迴文串,這個演算法的時間複雜度為O(n^3)的,顯然無法令人滿意,稍微優化的一個演算法是列舉迴文串的中點,這裡要分為兩種情況,一種是迴文串長度是奇數的情況,另一種是迴文串長度是偶數的情況,列舉中點再判斷是否是迴文串,這樣能把演算法的時間複雜度降為O(n^2),但是當n比較大的時候仍然無法令人滿意,Manacher演算法可以線上性時間複雜度內求出一個字串的最長迴文字串,達到了理論上的下界。

1.Manacher演算法原理與實現

下面介紹Manacher演算法的原理與步驟。

首先,Manacher演算法提供了一種巧妙地辦法,將長度為奇數的迴文串和長度為偶數的迴文串一起考慮,具體做法是,在原字串的每個相鄰兩個字元中間插入一個分隔符,同時在首尾也要新增一個分隔符,分隔符的要求是不在原串中出現,一般情況下可以用#號。下面舉一個例子:

(1)Len陣列簡介與性質

Manacher演算法用一個輔助陣列Len[i]表示以字元T[i]為中心的最長迴文字串的最右字元到T[i]的長度,比如以T[i]為中心的最長迴文字串是T[l,r],那麼Len[i]=r-i+1。

對於上面的例子,可以得出Len[i]陣列為:

Len陣列有一個性質,那就是Len[i]-1就是該回文子串在原字串S中的長度,至於證明,首先在轉換得到的字串T中,所有的迴文字串的長度都為奇數,那麼對於以T[i]為中心的最長迴文字串,其長度就為2*Len[i]-1,經過觀察可知,T中所有的迴文子串,其中分隔符的數量一定比其他字元的數量多1,也就是有Len[i]個分隔符,剩下Len[i]-1個字元來自原字串,所以該回文串在原字串中的長度就為Len[i]-1。

有了這個性質,那麼原問題就轉化為求所有的Len[i]。下面介紹如何線上性時間複雜度內求出所有的Len。

(2)Len陣列的計算

首先從左往右依次計算Len[i],當計算Len[i]時,Len[j](0<=j<i)已經計算完畢。設P為之前計算中最長迴文子串的右端點的最大值,並且設取得這個最大值的位置為po,分兩種情況:

第一種情況:i<=P

那麼找到i相對於po的對稱位置,設為j,那麼如果Len[j]<P-i,如下圖:

那麼說明以j為中心的迴文串一定在以po為中心的迴文串的內部,且j和i關於位置po對稱,由迴文串的定義可知,一個迴文串反過來還是一個迴文串,所以以i為中心的迴文串的長度至少和以j為中心的迴文串一樣,即Len[i]>=Len[j]。因為Len[j]<P-i,所以說i+Len[j]<P。由對稱性可知Len[i]=Len[j]。

如果Len[j]>=P-i,由對稱性,說明以i為中心的迴文串可能會延伸到P之外,而大於P的部分我們還沒有進行匹配,所以要從P+1位置開始一個一個進行匹配,直到發生失配,從而更新P和對應的po以及Len[i]。

第二種情況: i>P

如果i比P還要大,說明對於中點為i的迴文串還一點都沒有匹配,這個時候,就只能老老實實地一個一個匹配了,匹配完成後要更新P的位置和對應的po以及Len[i]。

2.時間複雜度分析

Manacher演算法的時間複雜度分析和Z演算法類似,因為演算法只有遇到還沒有匹配的位置時才進行匹配,已經匹配過的位置不再進行匹配,所以對於T字串中的每一個位置,只進行一次匹配,所以Manacher演算法的總體時間複雜度為O(n),其中n為T字串的長度,由於T的長度事實上是S的兩倍,所以時間複雜度依然是線性的。

下面是演算法的實現,注意,為了避免更新P的時候導致越界,我們在字串T的前增加一個特殊字元,比如說‘$’,所以演算法中字串是從1開始的。

複製程式碼
const int maxn=1000010;
char str[maxn];//原字串
char tmp[maxn<<1];//轉換後的字串
int Len[maxn<<1];
//轉換原始串
int INIT(char *st)
{
    int i,len=strlen(st);
    tmp[0]='@';//字串開頭增加一個特殊字元,防止越界
    for(i=1;i<=2*len;i+=2)
    {
        tmp[i]='#';
        tmp[i+1]=st[i/2];
    }
    tmp[2*len+1]='#';
    tmp[2*len+2]='$';//字串結尾加一個字元,防止越界
    tmp[2*len+3]=0;
    return 2*len+1;//返回轉換字串的長度
}
//Manacher演算法計算過程
int MANACHER(char *st,int len)
{
     int mx=0,ans=0,po=0;//mx即為當前計算迴文串最右邊字元的最大值
     for(int i=1;i<=len;i++)
     {
         if(mx>i)
         Len[i]=min(mx-i,Len[2*po-i]);//在Len[j]和mx-i中取個小
         else
         Len[i]=1;//如果i>=mx,要從頭開始匹配
         while(st[i-Len[i]]==st[i+Len[i]])
         Len[i]++;
         if(Len[i]+i>mx)//若新計算的迴文串右端點位置大於mx,要更新po和mx的值
         {
             mx=Len[i]+i;
             po=i;
         }
         ans=max(ans,Len[i]);
     }
     return ans-1;//返回Len[i]中的最大值-1即為原串的最長迴文子串額長度 
  }

相關推薦

Manacher演算法

Manacher演算法 演算法總結第三彈 manacher演算法,前面講了兩個字串相演算法——kmp和拓展kmp,這次來還是來總結一個字串演算法,manacher演算法,我習慣叫他 “馬拉車”演算法。 相對於前面介紹的兩個演算法,Manacher演算法的應用範圍要狹窄得多,但是它的思想和拓展k

[轉] Manacher演算法

const int maxn=1000010; char str[maxn];//原字串 char tmp[maxn<<1];//轉換後的字串 int Len[maxn<<1]; //轉換原始串 int INIT(char *st) { int i,len=s

manacher演算法+模板 P3805

  首先我們知道迴文子串的判定和長度的奇偶性是有關係的,由於迴文分為偶迴文(比如 bccb)和奇迴文(比如 bcacb),而在處理奇偶問題上會比較繁瑣,所以這裡我們使用一個技巧,在字元間插入一個字元(前提這個字元未出現在串裡),常用的是"$""#"。舉個例子:s="abbahopxpo",轉換為newS=

最長迴文子串-Manacher演算法()

定義: 迴文串:一個字串, 逆置之後,與原串相同; 迴文子串: 一個字串的子串(連續),是迴文串.則該子串為整個字串的一個迴文子串. 最長迴文子串:一個字串中最長的迴文子串.

hihocoder 1032 最長迴文子串 (Manacher演算法 +模板)

時間限制:1000ms 單點時限:1000ms 記憶體限制:64MB 描述    小Hi和小Ho是一對好朋友,出生在資訊化社會的他們對程式設計產生了莫大的興趣,他們約定好互相幫助,在程式設計的

php openssl_sign() 語法+RSA公私鑰加密解密,非對稱加密演算法

其實有時候覺得寫部落格好煩,就個函式就開篇部落格。很小的意見事情而已,知道的人看來多取一舉,或者說沒什麼必要,浪費時間,不知道的人就會很鬱悶。技術就是這樣的,懂的人覺得真的很簡單啊,不知道的人真的好難。。。 一般在跟第三方介面對接資料的時候,為了保證很多都使用的RSA簽名,沒性趣瞭解的同學只需要

Show, attend and tell演算法及原始碼

mark一下,感謝作者分享! https://blog.csdn.net/shenxiaolu1984/article/details/51493673 原論文:https://arxiv.org/pdf/1502.03044v2.pdf 原始碼:https://github.c

資料分析學習之不得不知的八大演算法

學習資料分析的朋友們都知道,演算法是不可或缺的,或者說演算法在一定程度上可以更好的量化的一個人的學習能力和水平,本文感謝科多大資料的馮老師,由他整理了經典的八大演算法,相關的資料希望能幫助大家瞭解。 演算法一:快速排序法 快速排序是由東尼 · 霍爾所發展的一種排序演算法。在平均狀況下,排序

程式設計思想 - 五大常用演算法

https://www.cnblogs.com/brucemengbm/p/6875340.html https://blog.csdn.net/changyuanchn/article/details/51476281 https://www.cnblogs.com/chuninggao/p/

Kadane演算法及求解最大子數列和問題

最大子數列和問題         給出一個數列,現在求其中一個子數列,要求是所有子數列的和的最大值。另外還有其他問法,例如給出一個數組,要求求出連續的元素和的最大值。可以一個例子來解釋: 假設有數列:[-1,2,3,-5,6,-2,4],那麼總共有

K-NN近鄰演算法

 K-近鄰演算法屬於一種監督學習分類演算法,該方法的思路是:如果一個樣本在特徵空間中的k個最相似(即特徵空間中最鄰近)的樣本中的大多數屬於某一個類別,則該樣本也屬於這個類別。  (1) 需要進行分類,分類的依據是什麼呢,每個物體都有它的特徵點,這個就是分類的依據,特徵點可

字典序演算法

一、字典序 字典序,就是按照字典中出現的先後順序進行排序。 1、單個字元 在計算機中,25個字母以及數字字元,字典排序如下: '0' < '1' < '2' < ... < '9' < 'a' < 'b' < ... &l

演算法 】 二維動態規劃

馬攔過河卒 原題傳送門 這一到題目也是比較基礎的動態規劃,也可以理解為是遞推,主要是運用加法原理,思維難度不大。我們要求從 (0,0) ( 0

圖的最小生成樹prim演算法

prim演算法是求圖的最小生成樹的一種演算法,它是根據圖中的節點來進行求解,具體思想大概如下: 首先,將圖的所有節點(我們假定總共有n個節點)分成兩個集合,V和U。其中,集合V儲存的是我們已經訪問過的節點,集合U儲存的是我們未曾訪問的節點。prim演算法第一步就是選定第一個節點放入集合

吳恩達機器學習課程筆記02——處理房價預測問題(梯度下降演算法

  建議記住的實用符號 符號 含義 m 樣本數目 x 輸入變數 y 輸出變數/目標變數

樹鏈剖分演算法

學OI也有一段時間了,感覺該搞點東西了。 於是學習了樹(熟)鏈(練)剖(pou)分(糞) 當然,學習這個演算法是需要先學習線段樹的。不懂的還是再過一段時間吧。 如果碰到一道題,要對一顆樹的兩個點中的最短路徑、以u為根的子樹之類的東西進行修改或者查詢,那麼大概就是樹鏈剖分的題了。 樹鏈剖分就是把一顆

各種排序演算法C++實現

1.氣泡排序 時間複雜度 O ( n

整數拆分演算法

問題描述 輸入一個N,輸出所有拆分的方式。如輸入3 輸出1+1+1 1+2 3 演算法思想 用一個數組res[]存放拆分的解,用全域性變數存放拆分的方法數。divN(n,k)使用n表示要分解的整數,k表示res陣列下標,即第k次拆分。先從divN(n,1)開始,用num表示第

演算法】對於單調棧的重新理解

對於單調棧的重新理解 關於什麼是單調棧和為什麼要用單調棧: 亂頭髮節 地平線 Largest Rectangle in a Histogram 關於什麼是單調棧和為什麼要用單調棧: 單調棧,就是棧中的元

遞迴演算法

1. 何為遞迴? 遞迴在我們的生活中其實很常見。假設你去電影院看電影,黑漆漆一片,你不知道自己來到了第幾排,於是你問前面的人他是第幾排,知道了前面的人是第幾排,加一也就是你所在的排數。但前面的人也不知道,於是他也繼續向前問,直到第一排的人回答他在第一排,然後再依次往後傳,最後