演算法進階之manacher演算法 (求最長迴文)

來源:互聯網
上載者:User

標籤:

前幾天bestcode做到一道字串的題目,需要O(n)的迴文,正好看到網上的manacher演算法,於是來學習一發


進入正題:

manacher演算法


用法:一般用於求一個字串的最大迴文,操作過程中會記錄以每個點為中心的迴文半徑,可用來進行其他動作

時間複雜度:O(n)

空間複雜度:記錄字串2*n,半徑數組2*n


內容:

記p[i]為以i為中心的迴文半徑(aba中以b為中心的迴文半徑為2,那麼如果是偶數個數的迴文呢,比如abba?這裡先討論奇數,偶數後面再說,簡單說就是用輸入鍵台變成奇數)

因此以下的部分討論的都是奇數個數的迴文>>>>>>>>>>>>>>

那麼無可避免先要for一遍,for(j=0;j<n;i++) 來求每個p[j],下面就是對p[j](令j=i+k)的求解

分析:假設已經求出了i+k前面的0~i+k-1的p,現在要求p[i+k],

 那麼首先根據前面的點求半徑的時候有沒有遍曆到了i+k這個點可分為兩大類:

一、 前面的點求半徑的時候還沒有遍曆到了i+k這個點,即p[i]+i<i+k

(設i為前i+k個點中半徑時遍曆到最靠近字串後面的點,不是迴文最長!)

那麼此時p[i+k]=1;

這時候說明此時的i+k還沒找過半徑,需要從頭開始找while(s[i+k+p[i+k]]==s[i+k-p[i+k]]) p[i+k]++;

二、 前面的點求半徑時已經遍曆到了i+k這個點,即p[i]+i>=i+k
(設i為前i+k個點中半徑時遍曆到最靠近字串後面的點,不是迴文最長!)

這時候i+k前面有關於i對稱的i-k,所以算i+k的迴文半徑時不需要從第一個開始找,那麼是從p[i-k]個開始找嗎?

答案是不一定的!

下面借用網上的圖片來分析一下可能出現的兩種情況:


<1>p[i]-k<p[i-k](此時p[i-k]>=p[i+k]),下面是結合圖片的分析


首先解釋一,圖中的紅線為i的迴文半徑,黑線為i求迴文半徑時遍曆過的點,深藍線是i-k的迴文半徑,那麼此時i+k的迴文半徑一定是橙線,為什麼呢?

首先橙線部分肯定是i+k的半徑的一部分,由於對稱性可以得到與左邊i-k的橙線部分相同,

那麼i+k的半徑可能大於橙線部分嗎?

假設i+k的迴文半徑大於橙線,如中橙線+紫線,那麼由於對稱性i-k也必定含有紫線部分,那麼此時p[i]就不再是原來的p[i]了,而是如的黑線+紫線,這個與之前矛盾,所以不可能存在


即p[i+k]=p[i]-k;

<2>p[i]-k>=p[i-k](此時p[i-k]<=p[i+k]),下面是結合圖片的分析


圖中的紅線為i的迴文半徑,黑線為i求迴文半徑時遍曆過的點,深藍線是i-k的迴文半徑,首先由於對稱性i+k的迴文半徑一定包含藍線部分,與此同時i+k的右邊可能還有字元能組成更長的迴文,最後形成圖中的橙線部分,因此此時P[i+k]>=p[i-k],因此可以先賦值p[i+k]=p[i-k],然後while(s[i+k+p[i+k]]==s[i+k-p[i+k]]) p[i+k]++;繼續往後面找。


於是由上可以總結出p[j]的求法:

void query_p(int n)                             //n為字串的長度{    int id = 0;                                 //id為上述i    p[0] = 1;    for(int j=1;j<n;j++)                        //j為上述i+k,所以k=j-id    {                       if(p[id]+id<=j) p[j]=1;                 //第一種情況        else p[j]=min(p[2*id-j],p[id]-(j-id));  //第二種情況的兩種小情況        while(s[j+p[j]]==s[j-p[j]]) p[j]++;     //繼續往後找        if(p[j]+j>p[id]+id) id=j;               //更新最靠近字串後面的點    }}

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<到這裡為止都是把迴文當做奇數個來算的,那麼如果出現偶數個怎麼辦呢,具體做法是利用輸入鍵台,字元可以用字串中肯定不會出現的字元來填充,具體做法是:

假設字串中只有字母,那麼可以利用特殊符號填充,填充方法是在每個字元的前面插入一個一樣的字元

比如填充字元時#時,原字串是abba,填充後的字元變為#a#b#b#a#,然後就可以了,至於字母的原始迴文半徑就是現在的p[i]/2+1,填充字元不會影響結果。

不過這裡需要注意的是:為了避免數組越界需要在填充後在字元再處理一下如上述*#a#b#b#a#‘\0‘,這樣從第一個字母開始遍曆的時候就不會出現問題了

這裡貼一個填充後的求最長迴文的代碼:

int for_max(int n)                             //n為原字串的長度{    for(int j=n;j>=0;j--)                       //輸入鍵台    {        s[j*2+2]=s[j];        s[j*2+1]='#';    }    s[0]='$';                                   //防止數組越界填充第一個字元和最後一個字元為‘$’和‘/0’    int id = 0;                                 //id為上述i    int maxlen = 0;    p[0] = 1;    for(int j=2;j<2*n+2;j++)                        //j為上述i+k,所以k=j-id    {        if(p[id]+id<=j) p[j]=1;                 //第一種情況        else p[j]=min(p[2*id-j],p[id]-(j-id));  //第二種情況的兩種小情況        while(s[j+p[j]]==s[j-p[j]]) p[j]++;     //繼續往後找        if(p[j]+j>p[id]+id) id=j;               //更新最靠近字串後面的點        maxlen=max(maxlen,p[j]-1);              //更新最長迴文    }    return maxlen;}



具體題目可以看HDU3068:題意大概為給一個字串,求最長回是多少

http://acm.hdu.edu.cn/showproblem.php?pid=3068

代碼如下:

#include <iostream>#include <cstdio>#include <cstring>#include <queue>#include <algorithm>#include<vector>#pragma comment(linker,"/STACK:1024000000,1024000000")using namespace std;const int maxn = 2.2e5+5;                         char s[maxn*2];                                 //2倍數組int p[maxn*2];int for_max(int n)                             //n為原字串的長度{    for(int j=n;j>=0;j--)                       //輸入鍵台    {        s[j*2+2]=s[j];        s[j*2+1]='#';    }    s[0]='$';                                   //防止數組越界填充第一個字元和最後一個字元為‘$’和‘/0’    int id = 0;                                 //id為上述i    int maxlen = 0;    p[0] = 1;    for(int j=2;j<2*n+2;j++)                        //j為上述i+k,所以k=j-id    {        if(p[id]+id<=j) p[j]=1;                 //第一種情況        else p[j]=min(p[2*id-j],p[id]-(j-id));  //第二種情況的兩種小情況        while(s[j+p[j]]==s[j-p[j]]) p[j]++;     //繼續往後找        if(p[j]+j>p[id]+id) id=j;               //更新最靠近字串後面的點        maxlen=max(maxlen,p[j]-1);              //更新最長迴文    }    return maxlen;}int main(){    int n;    while(scanf("%s",s)!=EOF){        int m=strlen(s);        printf("%d\n",for_max(m));    }}




著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

演算法進階之manacher演算法 (求最長迴文)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.