最長迴文子串-----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;
}