Manacher算法的詳細(xì)講解

Manacher算法,又叫“馬拉車”算法,可以在時(shí)間復(fù)雜度為O(n)的情況下求解一個(gè)字符串的最長(zhǎng)回文子串長(zhǎng)度的問題。

一、回文子串的一般解法

比較簡(jiǎn)單的思路是將字符串的每一個(gè)字符作為回文子串的中心對(duì)稱點(diǎn),每次保存前面求得的回文子串的最大值,最后得到的就是最長(zhǎng)的回文子串的長(zhǎng)度,這種方式的時(shí)間復(fù)雜度是O(n^2)。在求解過(guò)程中,基數(shù)的回文子串與偶數(shù)的回文子串是不一樣的。比如最長(zhǎng)回文子串為aba,對(duì)稱中心就是b,如果最長(zhǎng)回文子串為abba,則對(duì)稱中心應(yīng)該為兩個(gè)b之間,為了解決這個(gè)問題,可以在每個(gè)字符兩邊加上一個(gè)符號(hào),具體什么符號(hào)(是字符串里面的符號(hào)也行)對(duì)結(jié)果沒有影響,比如加上“#”,則上述的兩個(gè)序列變成了#a#b#a#和#a#b#b#a#,求出的長(zhǎng)度分別為6和9,再除以2就可以得到最后的結(jié)果3和4。這種方式的時(shí)間復(fù)雜度太高,下面介紹時(shí)間復(fù)雜度為O(n)的Manacher算法。

二、Manacher算法中的基礎(chǔ)概念

在進(jìn)行Manacher算法時(shí),字符串都會(huì)進(jìn)行上面的進(jìn)入一個(gè)字符處理,比如輸入的字符為acbbcbds,用“#”字符處理之后的新字符串就是#a#c#b#b#c#b#d#s#。

1、回文半徑數(shù)組radius

回文半徑數(shù)組radius是用來(lái)記錄以每個(gè)位置的字符為回文中心求出的回文半徑長(zhǎng)度,如下圖所示,對(duì)于p1所指的位置radius[6]的回文半徑是5,每個(gè)位置的回文半徑組成的數(shù)組就是回文數(shù)組,所以#a#c#b#b#c#b#d#s#的回文半徑數(shù)組為[1, 2, 1, 2, 1, 2, 5, 2, 1, 4, 1, 2, 1, 2, 1, 2, 1]。

要處理的字符串

2、最右回文右邊界R

一個(gè)位置最右回文右邊界指的是這個(gè)位置及之前的位置的回文子串,所到達(dá)的最右邊的地方。比如對(duì)于字符串#a#c#b#b#c#b#d#s#,求它的每個(gè)位置的過(guò)程如下:

最右回文右邊界R過(guò)程

最開始的時(shí)候R=-1,到p=0的位置,回文就是其本身,最右回文右邊界R=0;p=1時(shí),有回文串#a#,R=2;p=2時(shí),R=2;P=3時(shí),R=6;p=4時(shí),最右回文右邊界還是p=3時(shí)的右邊界,R=6,依次類推。

3、最右回文右邊界的對(duì)稱中心C

就是上面提到的最右回文右邊界的中心點(diǎn)C,如下圖,p=4時(shí),R=6,C=3

最右回文右邊界的對(duì)稱中心C

三、Manacher算法的流程

首先大的方面分為兩種情況:

第一種情況:下一個(gè)要移動(dòng)的位置在最右回文右邊界R的右邊。

比如在最開始時(shí),R=-1,p的下一個(gè)移動(dòng)的位置為p=0,p=0在R=-1的右邊;p=0時(shí),此時(shí)的R=0,p的下一個(gè)移動(dòng)位置為p=1,也在R=0的右邊。

在這種情況下,采用普遍的解法,將移動(dòng)的位置為對(duì)稱中心,向兩邊擴(kuò),同時(shí)更新回文半徑數(shù)組,最右回文右邊界R和最右回文右邊界的對(duì)稱中心C。

第二種情況:下一個(gè)要移動(dòng)的位置就是最右回文右邊界R或是在R的左邊

在這種情況下又分為三種:

1、下一個(gè)要移動(dòng)的位置p1不在最右回文右邊界R右邊,且cL<pL。

p2是p1以C為對(duì)稱中心的對(duì)稱點(diǎn);

pL是以p2為對(duì)稱中心的回文子串的左邊界;

cL是以C為對(duì)稱中心的回文子串的左邊界。

這種情況下p1的回文半徑就是p2的回文半徑radius[p2]。

p1<=R且cL<pL

2、下一個(gè)要移動(dòng)的位置票p1不在最右回文右邊界R的右邊,且cL>pL。

p2是p1以C為對(duì)稱中心的對(duì)稱點(diǎn);

pL是以p2為對(duì)稱中心的回文子串的左邊界;

cL是以C為對(duì)稱中心的回文子串的左邊界。

這種情況下p1的回文半徑就是p1到R的距離R-p1+1。

p1<=R且cL>pL

3、下一個(gè)要移動(dòng)的位置票p1不在最右回文右邊界R的右邊,且cL=pL;

p2是p1以C為對(duì)稱中心的對(duì)稱點(diǎn);

pL是以p2為對(duì)稱中心的回文子串的左邊界;

cL是以C為對(duì)稱中心的回文子串的左邊界。

這種情況下p1的回文半徑就還要繼續(xù)往外擴(kuò),但是只需要從R之后往外擴(kuò)就可以了,擴(kuò)了之后更新R和C。

p1<=R且cL==pL

四、Manacher時(shí)間復(fù)雜度分析

從上面的分析中,可以看出,第二種情況的1,2的求某個(gè)位置的回文半徑的時(shí)間復(fù)雜度是O(1),對(duì)于第一種情況和第二種情況的3,R是不斷的向外擴(kuò)的,不會(huì)往回退,而且尋找回文半徑時(shí),R之內(nèi)的位置是不是進(jìn)行判斷的,所以對(duì)整個(gè)字符串而且,R的移動(dòng)是從字符串的起點(diǎn)移動(dòng)到終點(diǎn),時(shí)間復(fù)雜度是O(n),所以整個(gè)manacher的時(shí)間復(fù)雜度是O(n)。

五、Manacher的代碼實(shí)現(xiàn)

public static void main(String[] args) {
        String str = "abcdcbafabcdck";
        //String str = "acbbcbds";
        System.out.println(manacher(str));
    }

    public static char[] manacherString(String str){
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            sb.append("#");
            sb.append(str.charAt(i));
        }
        sb.append("#");
        return sb.toString().toCharArray();
    }

    public static int manacher(String str){
        if(str == null || str.length() < 1){
            return 0;
        }
        char[] charArr = manacherString(str);
        int[] radius = new int[charArr.length];
        int R = -1;
        int c = -1;
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < radius.length; i++) {
            radius[i] = R > i ? Math.min(radius[2*c-i],R-i+1):1;
            while(i+radius[i] < charArr.length && i - radius[i] > -1){
                if(charArr[i-radius[i]] == charArr[i+radius[i]]){
                    radius[i]++;
                }else{
                    break;
                }
            }
            if(i + radius[i] > R){
                R = i + radius[i]-1;
                c = i;
            }
            max = Math.max(max,radius[i]);
        } 
        return max-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)容

  • 題目 有這么一個(gè)字符串s,找到s中最長(zhǎng)的回文子串,假設(shè)s的長(zhǎng)度最大值是1000.所謂回文就是中心軸對(duì)稱的字符串。 ...
    Stroman閱讀 495評(píng)論 0 1
  • 三十歲的你,被家庭瑣事摧殘的你,被熊孩子折磨得死去活來(lái)的你,還記得十四歲那年讓你怦然心動(dòng)的那個(gè)boy嗎? 1980...
    水清的minguo往事閱讀 1,152評(píng)論 4 4
  • 你永遠(yuǎn)也想不到我了解你這么多,即使我只和你見過(guò)兩次面,說(shuō)過(guò)三句話,看過(guò)一場(chǎng)電影。但是我知道你愛什么,做過(guò)什...
    北彩閱讀 628評(píng)論 0 0
  • 選擇性放棄要比永久性痛苦更可貴。 并不是所有的付出都會(huì)有回報(bào)。 有花無(wú)果,難得放空。 做不到克制,就選擇停止。 今...
    世俗凡人閱讀 254評(píng)論 2 1
  • 也許你的眼里,從未看清過(guò)我的樣貌,但我相信,在你心里,應(yīng)如我信任你一般信任我。 也許你不能再和許多喜愛你的人見面,...
    Daisy2lan閱讀 207評(píng)論 0 0

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