Manacher(馬拉車)算法

這名字很雷啊

這貌似是一個很高級的算法(藍模板哎!

作用就是以O(n)的時間複雜度來求解字符串中的最長迴文串(其實把所有迴文串都搞出來了)【我也不曉得爲什麼是O(n)(霧關於複雜度的問題可以看看它(我有點懵懶的想

這個算法的核心原理就是——

如果一個大回文串(準確的說是最靠右的),中點以左部分包含(好像說包含不太準確)一個小回文串,那麼它的中點以右也一定包含一個同樣(其實也不是完全一樣,看下面說明)的小回文串(誰說我是小回文串,我還有可能比這個大回文串還大咧

 

圖示如上(我畫了半個多小時QAQ),它反映了Manacher算法最爲精髓的部分 ,正是利用了以上原理(充分發揮已求出的迴文串的作用)。(PS:迴文長度(自己編的名字)指回文串的半徑(中點到一端的距離))

這個部分的代碼如下:

if (x<=maxright) {
    if (x+len[mid*2-x]>maxright) len[x]=maxright-x;
    else len[x]=len[mid*2-x];
}
else len[x]=1;
//不過是把圖中的步驟變現而已

所以說這個maxright,maxright,還有mid究竟是什麼呢?它們是一組變量

它們記錄了在此之前所查找到的最靠右的迴文串的最右側位置,最左側位置和中間點。

好了,在以上部分充分理解了之後,擺出Manacher操作部分的完整代碼:

maxright=0; mid=0;//其實不需要用到maxright 
for (i=0;i<n;i++) {
    j=mid*2-i;
    if (i<=maxright) {
        if (i+len[j]>maxright) len[i]=maxright-i;
        else len[i]=len[j];
    }
    else len[i]=1;
    while (t[i+len[i]]==t[i-len[i]]) len[i]++;//對當前的迴文串進行拓展(即當最左邊再左的字符與最右邊再右的字符相等,就把迴文串的半徑++)
    len[i]--;
    if (len[i]>ans) ans=len[i];//爲什麼ans=所有len中最大的-1?繼續往後看
    if (i+len[i]>maxright) {
        maxright=i+len[i];
        mid=i;
    }//更新maxright及mid
}

但是依然有一些問題,比如說mid可能並不會落在某個字符上,而落在兩個字符中間。(例:ab|ba)

所以要先進行一個預處理:在原串的基礎上增加一些#號。

例:abba->#a#b#b#a#

所以回答上面代碼中提出的問題:比如aba迴文串長是3,而加了#後#a#b#a#半徑爲4(3+1);abba串長是4,而加了#後#a#b#b#a#的半徑爲5(4+1)

當然,因爲要從第一個‘#’開始掃描(防止越界),所以要在s[0]放上一個東西(隨便一個符號‘#’,'$','%'都差不多)

預處理代碼如下:

 scanf("%s",s);
 n=strlen(s);
 t[0]='$'; t[1]='#';
 for (i=0;i<n;i++) {
     t[i*2+2]=s[i];
     t[i*2+3]='#';
 }
 n=n*2+2;
 t[n]='\0';
//很簡單

所以整個程序就告一段落了,其實似乎也不是很難的樣子。

(但沒看懂就死活看不懂,看懂了就恍然大悟)

(我就枯坐了一個多半小時想了很久才明白最上面那個東西,因爲dalao們寫的是三元運算符)

(也許是我太菜了,所以做幾個這樣的文章來鞏固)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章