3068 最長迴文(馬拉車演算法模板)
阿新 • • 發佈:2019-01-30
Problem Description
給出一個只由小寫英文字元a,b,c...y,z組成的字串S,求S中最長迴文串的長度.
迴文就是正反讀都是一樣的字串,如aba, abba等
Input
輸入有多組case,不超過120組,每組輸入為一行小寫英文字元a,b,c...y,z組成的字串S
兩組case之間由空行隔開(該空行不用處理)
字串長度len <= 110000
Output
每一行一個整數x,對應一組case,表示該組case的字串中所包含的最長迴文長度.
Sample Input
aaaa
abab
Sample Output
4
3
接下來要確定的就是通過j點所能確定的i點回文串的長度最多是多少。首先應該明確,如果i點跑到mx(id點回文串所確定的範圍邊界)外面去了,那麼j點無論如何縮減範圍都不可能是id迴文串的子串,就不滿足上面加粗的結論了。就一定只能從1開始慢慢試探。這就是當mx < i的時候,MP[i] = 1的原因了。
給出一個只由小寫英文字元a,b,c...y,z組成的字串S,求S中最長迴文串的長度.
迴文就是正反讀都是一樣的字串,如aba, abba等
Input
輸入有多組case,不超過120組,每組輸入為一行小寫英文字元a,b,c...y,z組成的字串S
兩組case之間由空行隔開(該空行不用處理)
字串長度len <= 110000
Output
每一行一個整數x,對應一組case,表示該組case的字串中所包含的最長迴文長度.
Sample Input
aaaa
abab
Sample Output
4
3
#include <iostream> #include <cstdio> #include <cstring> #define Max 110005 using namespace std; char Ma[Max*2]; int Mp[Max*2]; void Manacher(char s[],int len) { int l = 0; Ma[l++] = '$'; Ma[l++] = '#'; for (int i = 0;i < len; i++){ Ma[l++] = s[i]; Ma[l++] = '#'; } Ma[l] = 0; int mx = 0,id = 0; for (int i = 0;i < l; i++){ Mp[i] = mx>i?min(Mp[2*id-i],mx-i):1; while (Ma[i+Mp[i]] == Ma[i-Mp[i]])Mp[i]++; if (i+Mp[i] > mx){ mx = i+Mp[i]; id = i; } } } char s[Max]; int main () { while (scanf ("%s",s) != EOF){ int len = strlen(s),ans = 0; Manacher(s,len); for (int i = 0;i < 2*len+2; i++){ ans = max(ans,Mp[i] - 1); } cout << ans <<endl; } return 0; }
馬拉車演算法網上解析也很多,但主要一句話
Mp[i] = mx>i?min(Mp[2*id-i],mx-i):1;
大多說的含糊不清。MP[i]代表當前下標i為中心的字串的迴文串半徑,馬拉車主要就是優化了每次試探MP[i]的時候不一定需要從1開始慢慢向兩邊移動來試探。
id是已知的最長的迴文串的中心,我們可以發現i關於id對稱是j。由於i從2開始列舉過來,早就經過了j的位置,所以j位置的最長迴文串已經確定如圖所示,假設j的迴文串完全被id的迴文串所包圍,那麼,由迴文串關於中心點對稱的特性可以保證,i點的迴文串的長度最少就是j點回文串的長度。即如果迴文串的子串也是迴文串,那麼這個子串關於主串中心對稱而得的子串也是一個迴文串。
接下來還有兩種情況
一種就是上圖中,j所確定的迴文串完全被包含,即整個串都是其子串。那麼i的可確定迴文串範圍就是j的迴文串範圍,MP[i]就變成了MP[j]。
還有一種情況就是j的迴文串已經超出了mx的範圍
對於紅線以外的區域完全未知,所以必須將MP[j]減去紅線外的範圍才是i的可確定範圍。或者理解為只有兩端都去掉外面的部分之後,剩下的才是id迴文串的子串,才可以對稱過去成為i的迴文串。然後再在已確定的範圍基礎上向兩邊擴充套件。