部分圖片轉自:http://www.cnblogs.com/grandyang/p/4475985.html
manacher算法(民間稱馬拉車算法233)是用來找字符串中的最長回文子串的,先來說一下什么是回文串,像這樣“abcba”這樣一個字符串找到一個中間位置,然后分別向他的左邊和右邊相等的距離位置的字符是相同的,那么這個字符串就稱為回文串,“abcba”這個字符串的len為5是奇數,我們可以找到一個中間字符,然后進行搜索也可以找出來(當然時間復雜度是比較高的),但是當我們遇到一個長度為偶數的字符串時該怎么找中間字符呢,像這樣“abccba”,下面我們引入Manacher算法,這是一個可以將長度為奇數或偶數的字符串一起考慮的神奇算法
Manacher算法可以將長度為奇數和偶數的回文串一起考慮:在原字符串的相鄰字符串之間插入一個分隔符,字符串的首尾也要分別添加,注意分隔符必須是原字符串中沒有出現過的
原字符串s | a | b | a | b | c |
轉換后字符串str | # | a | # | b | # | a | # | b | # | c | # |
一、Len數組的簡單介紹
Manacher算法中用到一個非常重要的輔助數組Len[i]表示以str[i]為中心的最長回文子串的最右端到str[i]位置的長度,比如以str[i]為中心的最長回文串是str[l,r],那么Len[i]=r-i+1
轉換后的字符串str | # | a | # | b | # | a | # | b | # | c | # |
Len | 1 | 2 | 1 | 4 | 1 | 4 | 1 | 2 | 1 | 2 | 1 |
Len[i]數組有一個性質,Len[i]-1就等於該回文串在原串s中的長度
證明:在轉換后的字符串str中,所有的回文串的長度都是奇數,那么對於以str[i]為中心的最長回文串的長度為2*Len[i]-1,其中又有Len[i]個分隔符,所以在原字符串中的長度就是Len[i]-1,那么剩下的工作就是求Len數組
二、Len數組的計算
從左往右開始計算,假設0<=j<=i,那么在計算Len[i]時,Len[j]已經計算過了,設mx為之前計算過的最長回文串的右端點,id為取得這個端點值得位置(那么Len[id]=mx-id+1)
第一種情況:i<=mx.
找到i相對於id的對稱位置,設為j,再次分為兩種情況:
1、Len[j]<mx-i
mx的對稱點為2*id-mx,i和j所包含的范圍是2*Len[j]-1
那么說明以j為中心的回文串一定在以id為中心的回文串內部,且i和j關於id對稱,由回文串的定義可知,一個回文串反過來仍是回文串,所以以i為中心的回文串長度至少和以i為中心的回文串長度相等,即Len[i]>=Len[j].因為Len[j]<mx-i所以i+Len[j]<mx,由對稱性可知Len[i]=Len[j].
2、Len[j]>=mx-i
由對稱性說明以i為中心的回文串可能延伸到mx之外,而大於mx的部分我們還沒有進行匹配,所以要從mx+1位置開始一個一個匹配直到失配,從而更新mx和對應的id以及Len[i]
第二種情況,i>mx
如果i比mx還大,說明對於中點為i的回文串一點都沒匹配,這個時候只能一個個匹配(滑稽),匹配完成后更新mx的位置和對應的id及Len[i].
代碼實現:
#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int maxn=1e6+5; char s[maxn*2],str[maxn*2]; int Len[maxn*2],len; void getstr() { int k=0; str[k++]='$'; for(int i=0;i<len;i++) str[k++]='#', str[k++]=s[i]; str[k++]='#'; len=k; } void Manacher() { getstr(); int mx=0,id; for(int i=1;i<len;i++) { if(mx>i) Len[i]=min(Len[2*id-i],mx-i); else Len[i]=1; while(str[i+Len[i]]==str[i-Len[i]]) Len[i]++; if(Len[i]+i>mx) mx=Len[i]+i,id=i; } } int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",&s); len=strlen(s); Manacher(); int ans=1; for(int i=1;i<len;i++) ans=max(ans,Len[i]); printf("%d\n",ans-1); } return 0; }