Manacher算法總結


部分圖片轉自:http://www.cnblogs.com/grandyang/p/4475985.html

 

    manacher算法(民間稱馬拉車算法233)是用來找字符串中的最長回文子串的,先來說一下什么是回文串,像這樣“abcba”這樣一個字符串找到一個中間位置,然后分別向他的左邊和右邊相等的距離位置的字符是相同的,那么這個字符串就稱為回文串,“abcba”這個字符串的len為5是奇數,我們可以找到一個中間字符,然后進行搜索也可以找出來(當然時間復雜度是比較高的),但是當我們遇到一個長度為偶數的字符串時該怎么找中間字符呢,像這樣“abccba”,下面我們引入Manacher算法,這是一個可以將長度為奇數或偶數的字符串一起考慮的神奇算法

    Manacher算法可以將長度為奇數和偶數的回文串一起考慮:在原字符串的相鄰字符串之間插入一個分隔符,字符串的首尾也要分別添加,注意分隔符必須是原字符串中沒有出現過的

原字符串s a b a b c
轉換后字符串str # a # b # a # b # c #

 

一、Len數組的簡單介紹

    Manacher算法中用到一個非常重要的輔助數組Len[i]表示以str[i]為中心的最長回文子串的最右端到str[i]位置的長度,比如以str[i]為中心的最長回文串是str[l,r],那么Len[i]=r-i+1

轉換后的字符串str # a # b # a # b # c #
Len 1 2 1 4 1 4 1 2 1 2 1

 

    Len[i]數組有一個性質,Len[i]-1就等於該回文串在原串s中的長度

    證明:在轉換后的字符串str中,所有的回文串的長度都是奇數,那么對於以str[i]為中心的最長回文串的長度為2*Len[i]-1,其中又有Len[i]個分隔符,所以在原字符串中的長度就是Len[i]-1,那么剩下的工作就是求Len數組

二、Len數組的計算

    從左往右開始計算,假設0<=j<=i,那么在計算Len[i]時,Len[j]已經計算過了,設mx為之前計算過的最長回文串的右端點,id為取得這個端點值得位置(那么Len[id]=mx-id+1)

第一種情況:i<=mx.

    找到i相對於id的對稱位置,設為j,再次分為兩種情況:

        1、Len[j]<mx-i

        

       mx的對稱點為2*id-mx,i和j所包含的范圍是2*Len[j]-1

        那么說明以j為中心的回文串一定在以id為中心的回文串內部,且i和j關於id對稱,由回文串的定義可知,一個回文串反過來仍是回文串,所以以i為中心的回文串長度至少和以i為中心的回文串長度相等,即Len[i]>=Len[j].因為Len[j]<mx-i所以i+Len[j]<mx,由對稱性可知Len[i]=Len[j].

        2、Len[j]>=mx-i

        由對稱性說明以i為中心的回文串可能延伸到mx之外,而大於mx的部分我們還沒有進行匹配,所以要從mx+1位置開始一個一個匹配直到失配,從而更新mx和對應的id以及Len[i]

第二種情況,i>mx

        如果i比mx還大,說明對於中點為i的回文串一點都沒匹配,這個時候只能一個個匹配(滑稽),匹配完成后更新mx的位置和對應的id及Len[i].

代碼實現:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=1e6+5;
char s[maxn*2],str[maxn*2];
int Len[maxn*2],len;

void getstr()
{
    int k=0;
    str[k++]='$';
    for(int i=0;i<len;i++)
        str[k++]='#',
        str[k++]=s[i];
    str[k++]='#';
    len=k;
}
void Manacher()
{
    getstr();
    int mx=0,id;
    for(int i=1;i<len;i++)
    {
        if(mx>i) Len[i]=min(Len[2*id-i],mx-i);
        else Len[i]=1;
        while(str[i+Len[i]]==str[i-Len[i]]) 
            Len[i]++;
        if(Len[i]+i>mx)
            mx=Len[i]+i,id=i;
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",&s);
        len=strlen(s);
        Manacher();
        int ans=1;
        for(int i=1;i<len;i++) ans=max(ans,Len[i]);
        printf("%d\n",ans-1);
    }
    return 0;
}

 


免責聲明!

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



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