Manacher算法--求最長迴文子串

迴文和迴文子串

迴文串:順着讀和倒着讀都一樣的字符串;

迴文子串:給定字符串string,若str同時滿足以下兩個條件:1)str是string的子串;2)str是迴文串;那麼str就是string的迴文子串;

引出問題

要求求出上面string中最長的那個迴文子串;

解決方案

方案一:枚舉中心位置,對奇數位串和偶數位串分開處理;

int AllAlgorithms::longestPalindrome(const char *s, int n){
    int max,j;
    if (0 == s || n < 1) {
        return 0;
    }
    max = 0;
    
    for (int i = 0; i < n ; ++ i) {//i爲迴文串的中心
        for ( j = 0; ((i - j) >= 0) && ((i+j) < n); ++j) {//如果迴文串的長度是奇數
            if (s[i-j] != s[i+j]) {
                break;
            }
            if ((j * 2 + 1) > max) {
                max = 2*j + 1;
                
            }
        }
        for (j = 0; ((i-j) >= 0) && ((i+j+1) < n); ++j) {//如果迴文串的長度是偶數
            if (s[i-j] != s[i+j+1]) {
                break;
            }
            if ((j * 2 + 2) > max) {
                max = 2*j + 2;
                
            }

        }
    }
    return max;
    

}
該方案比較耗時累贅,因爲在判斷一個串是否是迴文串時,需要考慮奇數和偶數位的而不同,要分開處理,就造成了代碼的大量冗餘,因此該方法不可取;

方案二:Manacher算法

對於一個長度爲n的字符串,考慮如下問題:

長度爲n的字符串,一定有n-1個“鄰接間隙”,在加上首字符的前面,以及尾部字符的後面,一共有n-1+2=n+1個“gap”,然後再加上字符串本身,一共有字符n+n+1=2n+1個,這樣一擴展,可以發現,它一定爲奇數,因此這樣做了擴展後,我們可以只考慮奇數的情況,無需考慮偶數的情況,大大簡化了代碼;

下面我們就來介紹有關Manacher算法的解決思路是什麼樣的:

介紹之前先給出兩個trick:

1)爲處理一致,在字符串最前面加上一位未出現過的字符,如$:


2)用一個數組P[i]來記錄以字符S[i]爲中心的最長迴文子串向左或向右擴張的長度(包括S[i]),如:



算法的任務:在P[0...i-1]已知的前提下,計算P[i]的值,換句話說就是,在P[0...i-1]已知的前提下,能否爲P[i]的計算提供有用的信息;

算法核心:

1)簡單遍歷,得到i個三元組{k,P[k],k+P[k]},0≤k≤i-1;以k爲中心的字符形成的最大回文子串的最右位置是k+P[k]-1;

2)以k+P[k]爲關鍵字,找出上述i各三元組中,k+P[k]最大的那個三元組,記作(id,P[id],id+P[id]),爲了簡化起見,進一步記mx=id+P[id],於是得到三元組爲(id,P[id],mx),含義就是:在上述遍歷得到的所有三元組中,向右到達最遠的位置,就是mx;

3)在計算P[i]的時候,考查i是否落在了區間[0,mx)中:若i落在區間[0,mx)的右側,說明區間[0,mx)沒能控制住i,P[0...i-1]已知的前提下不能爲P[i]的計算提供有用的信息;若i落在mx的左側,則說明[0,mx)控制住了i,可以提供一些有用的信息;

下面我們用圖示來看一下Manacher算法的遞推關係:

1)


記i關於id的對稱點爲j,j=2*id-i,如果此時滿足:mx-i >P[j],則:

記my爲mx關於id的對稱點:my = 2*id-mx;

因爲以S[id]爲中心的最大回文子串爲:S[my+1...id...mx-1],而S[my+1...id]與S[id...mx-1]對稱,i和j也關於id對稱,所以P[i]=P[j];

2)


上圖中,同樣的:

記i關於id的對稱點爲j,j=2*id-i,如果此時滿足:mx-i < P[j],則:

記my爲mx關於id的對稱點:my = 2*id-mx;

因爲以S[id]爲中心的最大回文子串爲:S[my+1...id...mx-1],而S[my+1...id]與S[id...mx-1]對稱,i和j也關於id對稱,所以P[i]至少是等於上圖中綠色框部分,即等於mx-i;

void AllAlgorithms::Manacher(char *s, int *p){
    int size = (int)strlen(s);
    p[0] = 1;
    int id = 0,mx = 1;
    for (int i = 1; i < size; i ++) {
        if (mx > i) {
            p[i] = min(p[2*id - i], mx- i);
        }
        else
            p[i] = 1;
        for (; s[i+p[i]] == s[i- p[i]]; p[i] ++);
        
        if (mx < i + p[i]) {
            mx = i + p[i];
            id = i;
        }
    }
    
}

int AllAlgorithms::min(int a, int b){
    return ((a+b) - abs(a-b))/2;
}




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