我終於明白了的馬拉車算法———感謝我們學校的大佬


A:馬拉車是什么?

Q:是一種求回文子串(你也可以用它來去最長回文子串)的方法(速度很快)

A:有什么意義?

Q:證明了學好寫暴力,走遍天下都不怕的道理

馬拉車算法的精髓就是把之前匹配過的字符串結果放到后面來使用

小技巧

一個回文串它的對稱中心可能是某個字符(aba),也可能是某兩個字符之間(aa),理論上我們應該分類討論對吧?但實際上我們很懶,所以我們把字符串變成這樣(#a#a#)(#a#b#a#)

這樣一來,無論是什么字符串,它的長度都是奇數,只需要一種枚舉方式就可以了(奇數的枚舉方式,但同時可以枚舉的偶數的情況)

中心操作:枚舉對稱軸,有對稱軸向左右兩邊枚舉

r[i]表示以i為中點,向左右擴展回文串,所能到達的最大半徑是多少

由於我們是從右往左枚舉馬拉車,所以從0~pos的所有數的最長半徑都已經做出來了,是可用的

0~max_r就是我們已知的最大范圍

在這個基礎上,我們在pos~max_r的范圍枚舉i,並做j是關於pos的對稱軸,r[j]是已知的

接下來我們開始求關於r[i],當i+r[i]<max這個時候我們就可以用之前求過的r[j]來覆蓋i這個位置,減少了冗余計算(回文串的對稱性)

但不排除會存在i+r[i]>max_r 的情況,這個時候就不在我們的控制范圍之內了,那我們怎么辦?暴力枚舉。

在把pos轉到i的位置繼續執行上述操作

具體代碼如下

void Manacher(){
    for (int i=0;t[i];++i,len+=2){
        s[i<<1]='#';
        if (t[i]>='A'&&t[i]<='Z') s[i<<1|1]=t[i]-'A'+'a';
        else s[i<<1|1]=t[i];
    }
    s[len++]='#';
    int max_r=0,pos=0;
    for (int i=0;i<len;++i){
        if (max_r>i) r[i]=min(max_r-i,r[2*pos-i]);
        else r[i]=0;
        while (i+r[i]+1<len&&i-r[i]-1>=0&&s[i+r[i]+1]==s[i-r[i]-1]) r[i]++;
        if (r[i]+i>max_r){
            max_r=r[i]+i;
            pos=i;
        }
    }
}
//馬拉車

最后掃一遍r[i]求出max_r[i],max_r[i]-1就是我們要的最長回文子串的長度


免責聲明!

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



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