leetcode5:最長迴文子串

  這個題目主要是運用manacher算法,又稱爲馬拉車算法,下面對算法進行介紹。

manacher算法

  首先,定義一些變量,假設當前訪問的是第i 個位置:
    1.p[i]: 表示以第i 個字符爲中心的最長迴文子串的半徑。
    2.maxRight:i 之前(包括i )所對應的迴文子串所能到達的最右邊的位置。

  然後假設現在要求以第j(j=i+k) 個字符爲中心的最長迴文串的半徑p[j] ,假設在j 之前,是以第i 個字符爲中心的最長迴文串能夠到達最右邊的位置,即位置maxRight ,則說明maxRight=i+p[i] 。要快速的求出以第j 個字符爲中心的最長迴文子串半徑,一定要利用好之前已經求得的迴文子串。對j 的位置進行討論:
  1.j>=maxRight: 由於maxRight=i+p[i] ,所以j 所處的位置不在第i 個字符爲中心的最長迴文子串中,無法利用以前求得的經驗,這是得用一般的方法來求p[j] ,即初始化p[j]=1 ,因爲此時只包含本身。然後依次判斷左右兩邊的字符是否相等,來確定p[j] 的最長子串半徑大小。

  2.j<maxRight: 由於maxRight=i+p[i] ,所以j 所處的位置在第i 個字符爲中心的最長迴文子串中。此時,我們可以利用迴文串的稱關係來求解。因爲已經假設:j=i+k ,所以求p[j]p[i+k] 時,可以利用其關於i 的對稱點ik 所對應的p[ik] 值來求解。此時需要對(ikp[ik])(ip[i]) 的大小分三種情況來討論。

  2.1:(ikp[ik])>(ip[i]): 如圖所示

這裏寫圖片描述

此時p[ik] 對應的最長迴文串被p[i] 對應的最長迴文串的左半部分完全包圍,根據迴文的對稱性,p[i+k] 也應該被右半部分完全包圍,並且p[ik]=p[i+k]=p[j] ,很好理解,不需要證明。

  2.2:(ikp[ik])=(ip[i]): 如圖所示

這裏寫圖片描述

此時p[ik] 對應的最長迴文串還是被p[i] 對應的最長迴文串所包圍,所以對應的p[i+k] 的最長迴文串至少也會到達i+p[i] 的位置,即maxRight 處,但是因爲maxRight 位置之後的字符情況未知,可能還會有與以i+k 爲中心的左邊位置的字符相等,所以p[i+k] 的值可能還會更長,此時需要再次比較ch[left]==ch[right] 的情況來判斷是否會更長,此時p[i+k]>=p[ik] ,應該從(i+kp[ik])(i+k+p[ik]) 兩個位置進行左右比較。

  2.3:(ikp[ik])=(ip[i]): 如圖所示

這裏寫圖片描述

此時,p[i] 對應的最長迴文子串的左半部分無法完整的包含p[ik] 對應的最長迴文子串,此時p[i+k]=p[i]k ,即最長迴文子串半徑僅僅包含ip[i]ik 之間的那一小段。能不能更長呢,答案是否定的,下面給予證明。
  假設p[i+k] 的值能夠更大,即p[i+k]>p[i]k ,如圖所示,這相當於在(i+p[i]) 的位置後面延伸了一小段(圖中最右邊的那段小粗橫線),根據根據對稱性,圖中另外幾段小粗黑線必須也有的。假設從左到右依次標註那幾段小黑線爲L1,L2,L3,L4 ,現在假設L4 是存在的,假設其值爲ABC ,則根據迴文的對稱性,L3 的值爲CBAL2 的值爲ABCL1 的值爲CBA ,可以發現L1L4 也是迴文對稱的,所以以位置i 爲中心的最長迴文串應該更長,不應該是p[i] ,而應該是p[i]+3 ,因爲ABC 的長度爲3,但是這明顯最原始的假設不符合,因爲最原始假設就是爲p[i] 。所以p[i+k] 只能等於p[i]k

  然後對上述三種情況進行彙總:

p[i+k]=p[ik],p[ik],p[i]k,p[i]-k>p[i-k]p[i]-k=p[i-k]p[i]-k<p[i-k]

觀察總結之後發現,p[i+k]=min{p[ik],p[i]k} ,當然,對於第二種情況,即可能更長的情況,其實可能會更大,我們將上面三種也統一,在賦值完之後,在進行左右比較,因爲對於第一種和第三種情況,進行左右比較時肯定不相等,不影響值的變化,對於第二種情況進行左右比較就可以得到真實最長半徑。

2.字符串預處理

  在求最長迴文子串的時候,我們還得對區分以單字符爲中心的最長迴文子串和以雙字符爲中心的最長迴文子串進行判斷,爲了便於處理,我們可以通過在字符串中加入統一的字符,將奇偶情況統一爲以單字符爲中心的最長子串。例如:對於字符串“abbc”,我們可以通過加入’#’字符,將其變爲”#a#b#b#c#”,這樣就變爲以單字符爲中心;對於字符”aba”,也通過加入’#’,將其變爲”#a#b#a#”,仍然是以單字符爲中心。就完成了統一,也便於使用manacher算法,因爲該算法就是以單字符爲中心。
  但是由於最左邊和最右邊都是’#’,這樣在進行左右判斷的時候,就會leftright++ 成立,會出現越界的情況。爲了避免這種越界的發生,我們可以在兩端再次添加兩個不同的字符,例如最左邊添加’@’,最右邊添加’=’,此時”aba”處理之後的結果爲”@#a#b#a#=”。

3.代碼實現

    public static String solve(String ss) {
        /*
         * 加#號,使得兩種情況統一處理
         * 如"aa"變爲"#a#a#"
         * 再如"aba"變爲"#a#b#a#"
         * 防止越界,在首部再加上@,尾部加上$,只要不同即可,防止越界
         */

        char[] cs = ss.toCharArray();
        int len = cs.length * 2 + 3;                     //首部,尾部加一個不同的字符,防止越界

        char[] res = new char[len];
        res[0] = '@';
        res[len - 1] = '$';
        for(int i = 1; i < len - 1; i++) {
            if((i&1) != 0) {                            //如果是奇數
                res[i] = '#';
            }
            else {                                      //偶數
                res[i] = cs[i / 2 - 1];
            }
        }


        int maxRight = 0;                               //能到的最右邊           
        int longestCenter = 0;                          //最長的中心
        int longest = 0;                                //最長
        int nowCenter = 0;                              //到達最右邊對應的中心
        int[] dp = new int[len];                        //以i位置就中心的最長迴文串

        for(int i = 1; i < len - 1; i++) {
            if(maxRight > i) {
                dp[i] = Math.min(dp[2*nowCenter - i], maxRight - i);
            }
            else {
                dp[i] = 1;
            }

            //考慮以此爲中心,繼續遞增,因爲恰好在邊界上需要考慮
            while(res[i + dp[i]] == res[i - dp[i]]) {
                dp[i]++;
            }

            if(dp[i] + i > maxRight) {
                maxRight = dp[i] + i;
                nowCenter = i;
            }

            if(longest < dp[i]) {
                longest = dp[i];
                longestCenter = i;
            }
        }

        StringBuffer sb = new StringBuffer();
        for(int i = longestCenter - longest + 1; i < longestCenter + longest - 1; i++) {
            if(res[i] != '#')
                sb.append(res[i]);
        }


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