1. 程式人生 > >最長迴文子串-----Manacher演算法

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

迴文串是指aba、abba、cccbccc、aaaa這種左右對稱的字串。

輸入一個字串Str,輸出Str裡最長迴文子串的長度。

Input

輸入Str(Str的長度 <= 100000)

Output

輸出最長迴文子串的長度L。

Input示例

daabaac

Output示例

5

Manacher演算法

用於求最長迴文子串長度

轉換

首先 迴文子串分兩種  abba   和  aabaa  也就是奇數串和偶數串

對於奇數串 我們好判斷  只要找到中間點 例如aabaa裡面的b  然後向兩邊依次比較 即可判斷是否迴文了

所以 我們通過轉換

abba  -->   #a#b#b#a#  (9個字元)        aabaa-->  #a#a#b#a#a#  (11個字元)

通過上述轉換將奇數偶數串全部轉換為了 奇數串

演算法原理

演算法原理重點基於求一個len[]陣列

len[ i ] =4 代表以 i 為中心的最長迴文子串的長度半徑為5

例如 串  aaaba

而我們要求的答案 --> 這個串的最長迴文子串長度---> 就是len陣列中最大值-1

所以 現在問題就轉化成了 求解len陣列

求解len陣列

我們從左往右依次計算Len[i],當我們計算到  len[ i ] 時,i 前面的len值我們肯定已經計算完畢。

那麼 當我們計算len[ i ]的時候  

我們需要知道 兩個值  po 和 mx   

po是   i 前面的最長迴文子串的中心點的位置

mx是  以po為中心的最長迴文子串的右邊界  也就是 mx=po+len[ po ];

知道了這兩個值     計算len[ i ]的時候   我們還需要處理兩種情況

第一   i < mx

         當i<mx的時候   我們找到 i 關於po對稱的點 j ;j = 2 * po - i

         如果len[ j ] < mx - i   那就說明以j為中心的迴文串一定在以po為中心的迴文串的內部,i j 對稱。所以len[i]=len[j]

         如果len[ j ] >= mx - i  那就說明以i為中心的迴文串可能會延伸到mx之外 而mx到i之間的這段肯定是匹配的

                      所以len[ i ]先等於 mx - i,然後mx之外的我們再一個一個匹配

第二  i >= mx

那就說明對於中點為 i 的迴文串還一點都沒有匹配,我們就一個一個滿滿匹配好了。

匹配完成後要更新mx的位置和對應的po以及len[i]。

總體時間複雜度O(n)

程式碼實現

//51nod1089
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1e5+7;
char s[maxn];
char s_new[maxn<<1];
int len[maxn<<1];
int Init(){//轉換字串
    int len = strlen(s);
    s_new[0] = '$';
    s_new[1] = '#';
    int j = 2;
    for (int i = 0; i < len; i++){
        s_new[j++] = s[i];
        s_new[j++] = '#';
    }
    s_new[j] = '\0';
    return j;//返回新串的長度
}
int Manacher()
{
    int l=Init();  // 取得新字串長度並完成向 s_new 的轉換
    int max_len=-1;  // 最長迴文長度
    int po;  // 中心點
    int mx=0;//最長迴文子串半徑
    for (int i = 1; i < l; i++){
        if(mx>i)
            len[i]=min(len[2*po-i],mx-i);
        else
            len[i]=1;

        while(s_new[i-len[i]]==s_new[i+len[i]])len[i]++;//一個一個匹配

        if(len[i]+i>mx){//更新mx和po的值
            mx=len[i]+i;
            po=i;
        }
        max_len=max(max_len,len[i]-1);//記錄len陣列最大值
    }
    return max_len;
}

int main()
{
    scanf("%s",s);
    printf("%d\n",Manacher());
    return 0;
}