Manacher算法「最長(zhǎng)回文字符串」

Manacher算法原理

  • 最長(zhǎng)回文字符串包括奇數(shù)長(zhǎng)的和偶數(shù)長(zhǎng)的,求的時(shí)候都要分情討論,Manacher算法做了一個(gè)簡(jiǎn)單的處理,很巧妙地把奇數(shù)長(zhǎng)度回文串與偶數(shù)長(zhǎng)度回文串統(tǒng)一考慮,也就是在每個(gè)相鄰的字符之間插入一個(gè)分隔符,串的首尾也要加,當(dāng)然這個(gè)分隔符不能再原串中出現(xiàn),一般可以用‘#’字符。
    這樣一來,原來的奇數(shù)長(zhǎng)度回文串還是奇數(shù)長(zhǎng)度,偶數(shù)長(zhǎng)度的也變成以‘#’為中心奇數(shù)回文串了。
    其次給每個(gè)字符串首部加一個(gè)題目中不會(huì)出現(xiàn)的字符,避免處理越界問題。(一般加’$’)


    如圖字符串S就是通過Manacher算法預(yù)處理得到的新串
    P數(shù)組記錄的以 S[i]為中間S字符的回文串向右能匹配的最大長(zhǎng)度(包括S[i]這個(gè)字符).
    當(dāng)S[i]不是所加字符‘#’時(shí),P[i]-1就是字符串長(zhǎng)度了

  • 假設(shè)以i為中心的回文串長(zhǎng)度為len,因?yàn)?img class="math-inline" src="https://math.jianshu.com/math?formula=p%5B%20i" alt="p[ i" mathimg="1">]記錄以 S[i]為中間字符的回文串向右能匹配的長(zhǎng)度,所以有
    len=2*p[ i ]-1;
    又因?yàn)榇藭r(shí)串中加了其他字符#,以s[i]為中心的回文串一定是以#開頭和結(jié)尾的,以#為中間字符的就是長(zhǎng)度為偶數(shù)的,以非#號(hào)為中間字符的就是長(zhǎng)度為奇數(shù)的,例如“#b#b#”“#b#a#b#”所以減去最前或者最后的‘#’字符就是原串中長(zhǎng)度的2倍,即原串長(zhǎng)度為(S-1)/2,化簡(jiǎn)的P[i]-1。

Manacher算法實(shí)現(xiàn)

  • Manacher算法實(shí)現(xiàn)就是要找出p數(shù)組,如何找出P數(shù)組是關(guān)鍵.

首先從左往右依次計(jì)算P[i],當(dāng)計(jì)算P[i]時(shí),P[j](0<=j<i)已經(jīng)計(jì)算完畢。設(shè)mx為之前計(jì)算中最長(zhǎng)回文子串的右端點(diǎn)的最大值,并且設(shè)取得這個(gè)最大值的位置為id,分兩種情況:

情況1:

  1. i<=mx
    那么找到i相對(duì)于id的對(duì)稱位置,設(shè)為j,那么如果p[j]<mx-i
    因?yàn)?img class="math-inline" src="https://math.jianshu.com/math?formula=i%2Bj%3D2*id" alt="i+j=2*id" mathimg="1">,j能向左匹配的最左端點(diǎn)的坐標(biāo)是j-p[j]+1
    所以可知p[j]+2*id<mx+j,所以可知2*id-mx<j-p[j]+1
    向如下圖:

    那么說明以j為中心的回文串一定在以id為中心的回文串的內(nèi)部,且ji關(guān)于位置id對(duì)稱,那么意味著以S[i]為中間字符的回文串能向右匹配最右端點(diǎn)沒有超過mx。由回文串的定義可知,一個(gè)回文串反過來還是一個(gè)回文串,所以以i為中心的回文串的長(zhǎng)度至少和以j為中心的回文串一樣,所以P[i]=P[j]。
  2. 如果P[j]>=mx-i,由對(duì)稱性,說明以i為中心的回文串可能會(huì)延伸到mx之外,而大于mx的部分我們還沒有進(jìn)行匹配,所以要從mx+1位置開始一個(gè)一個(gè)進(jìn)行匹配,直到發(fā)生失配,從而更新mx和對(duì)應(yīng)的id以及P[i]。

    雖然p[j]>mx-i,但是可以保證的是i<->mx這一段是跟2*id-mx<->j是一樣的,說明以s[i]為中心的回文串至少有mx-i這么長(zhǎng)

情況2:
i>mx
如果i比mx還要大,說明對(duì)于中點(diǎn)為i的回文串還一點(diǎn)都沒有匹配,這個(gè)時(shí)候,就只能老老實(shí)實(shí)地一個(gè)一個(gè)匹配了,匹配完成后要更新mx的位置和對(duì)應(yīng)的id以及P[i]


        int len=strlen(s);
        int mx=0;
        int id=0;
        int res=-1;
        for(int i=len;i>=0;i--)
        {
            s[i*2+2]=s[i];
            s[i*2+1]='#';
        }
        s[0]='$';//加上特殊字符防止越界
        for(int i=0;i<=2*len+1;i++)
        {
            if(mx>i)//mx<i的情況
            {
                p[i]=min(p[2*id-i],mx-i);//比較p[j]大還是mx-i大,取小的
            }
            else//如果i>=mx,要從頭開始匹配
            {
                p[i]=1;
            }
            while(s[i-p[i]]==s[i+p[i]])//一個(gè)一個(gè)比較
            {
                p[i]++;
            }
            if(p[i]+i>mx))//若新計(jì)算的回文串右端點(diǎn)位置大于mx,要更新id和mx的值

            {
                mx=p[i]+i;
                id=i;
            }
            res=max(res,p[i]-1);
        }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容