馬拉車算法詳解


簡述

  Manacher算法,又稱馬拉車算法,它是用於求一個字符串的最長回文子串長度的算法,時間和空間復雜度為O(n)。

算法思想

  求一個字符串的最長回文子串長度,我們如果用暴力來做,我們就要取出這個串的所有子串,然后判斷這個子串是不是回文串,復雜度是n方的。

  那么馬拉車為何如此神奇能做到O(n)呢?

  首先我們來看這兩個串:abba和abcba。第一個串的回文中心在兩個b之間,第二個串的回文中心為字符c,這樣兩種情況我們分類討論太麻煩了,所以我們考慮對原字符串進行一個字符填充,abba->@#a#b#b#a# ,abcba->@#a#b#c#b#a#,這樣他們都變成了奇數,討論的情況就一致了。

  設原字符串長度為len1,重構后長度為len2,則len2=len1*2+2且對於s[i],它在重構后字符串的對應下標為i*2+2。

  由此我們可以寫出重定義字符串的代碼:

int getstr()
{//重定義字符串,s為原字符串,str為新串,len為s串長度,返回新串長度 
    int k=0,len=strlen(s);
    str[k++]='@';
    for(int i=0;i<len;i++){
        str[k++]='#';
        str[k++]=s[i];
    } 
    str[k++]='#';
    return k;
}

  首先我們規定幾個變量的含義:len[i]為以i為中心點的最長回文序列的回文半徑,po為當前中心點,p為當前回文子串的右邊界。  

  我們可以得出,ans=len[i]-1,因為加入填充字符后的str的最長回文串為2*len[i]-1,又因為#號的個數比原字符多一個,所以#字符的總數為len[i]個,故原字符的最長回文子串為len[i]-1個。

  當i<=p時,找到i相對於po的對稱點j,如果len[j]<p-i,如下圖:

  

  由對稱性得知,i和j左右的字符是一樣的,所以len[i]=len[j]。

  如果len[j]>=p-i,由對稱性,說明以i為中心的回文串會擴散至p以外,而大於p部分的串我們還沒用匹配,所以我們一個一個匹配,更新po和len[i]。

  

  當i>p時,說明以i為中心的回文串一點都沒匹配,就只能老老實實一個一個匹配了。

  

  所以我們可以寫出如下匹配部分代碼:

int manacher(int len){//len為str串長度 
    int mx=0,id;
    int maxx=0;
    for(int i=0;i<len;i++){
        if(i<mx) num[i]=min(mx-i,num[2*id-i]);//i在mx左邊,取在邊界以內且較小的那段 
        else num[i]=1;//i在mx右邊,所以直接等於1 
        while(str[i+num[i]]==str[i-num[i]]) num[i]++;//向右一個個匹配 
        if(i+num[i]>mx){
            mx=i+num[i];
            id=i;
            maxx=max(maxx,num[i]-1);
        }
    }
    return maxx;
}

模板

#include<iostream>
#include<string.h> 
using namespace std;
const int maxn=11000002;
char s[maxn<<1],str[maxn<<1];
int num[maxn*2];
int getstr()
{
    int k=0,len=strlen(s);
    str[k++]='@';
    for(int i=0;i<len;i++){
        str[k++]='#';
        str[k++]=s[i];
    } 
    str[k++]='#';
    return k;
}
int manacher(int len){
    int mx=0,id;
    int maxx=0;
    for(int i=0;i<len;i++){
        if(i<mx) num[i]=min(mx-i,num[2*id-i]);
        else num[i]=1;
        while(str[i+num[i]]==str[i-num[i]]) num[i]++;
        if(i+num[i]>mx){
            mx=i+num[i];
            id=i;
            maxx=max(maxx,num[i]-1);
        }
    }
    return maxx;
} 
int main()
{
    int cas=1;
    while(cin>>s){
        if(s[0]=='E') break;
        int len1=strlen(s);
        int len2=getstr();
        cout<<"Case "<<cas++<<": "<<manacher(len2)<<'\n';
    } 
    return 0;
}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM