manacher算法----O(n)最长回文串
manacher算法----O(n)最長回文串
???
manacher的時間復雜度為O(n),后綴數組好像可以處理O(nlogn),但是有些變態題目可能卡logn。不過這個算法還算比較容易理解的。
算法基本要點:首先用一個非常巧妙的方式,將所有可能的奇數/偶數長度的回文子串都轉換成了奇數長度:在每個字符的兩邊都插入一個特殊的符號。比如 abba 變成 #a#b#b#a#, aba變成 #a#b#a#。 為了進一步減少編碼的復雜度,可以在字符串的開始加入另一個特殊字符,這樣就不用特殊處理越界問題,比如$#a#b#a#。
下面以字符串12212321為例,經過上一步,變成了 S[] = "$#1#2#2#1#2#3#2#1#";
然后用一個數組 P[i] 來記錄以字符S[i]為中心的最長回文子串向左/右擴張的長度(包括S[i]),比如S和P的對應關系:
S???? #? 1? #? 2? #? 2? #? 1? #? 2? #? 3? #? 2? #? 1? #
P??? ?1? ?2? 1? 2? 5?? 2? 1? 4?? 1? 2? 1??6? ?1? 2?? 1? 2? 1
(p.s. 可以看出,P[i]-1正好是原字符串中回文串的總長度)【因為添加的#要比原回文字符串多1,所以減1】
下面計算P[i],該算法增加兩個輔助變量id和mx,其中id表示最大回文子串中心的位置,mx則為id+P[id],也就是最大回文子串的邊界。
這個算法的關鍵點就在這里了:如果mx > i,那么P[i] >= MIN(P[2 * id - i], mx - i)。
具體代碼如下:
if(mx > i) {p[i] = (p[2*id - i] < (mx - i) ? p[2*id - i] : (mx - i)); } else {p[i] = 1; }
當 mx - i > P[j] 的時候,以S[j]為中心的回文子串包含在以S[id]為中心的回文子串中,由于 i 和 j 對稱,以S[i]為中心的回文子串必然包含在以S[id]為中心的回文子串中,所以必有 P[i] = P[j],見下圖。
當 P[j] > mx - i 的時候,以S[j]為中心的回文子串不完全包含于以S[id]為中心的回文子串中,但是基于對稱性可知,下圖中兩個綠框所包圍的部分是相同的,也就是說以S[i]為中心的回文子串,其向右至少會擴張到mx的位置,也就是說 P[i] >= mx - i。至于mx之后的部分是否對稱,就只能一個一個匹配了。
對于 mx <= i 的情況,無法對 P[i]做更多的假設,只能P[i] = 1,然后再去匹配了
下面給出原文,進一步解釋算法為線性的原因
[cpp]?view plain?copy ?
- #include<stdio.h>??
- #include<string.h>??
- #include?<iostream>??
- #include?<cstring>??
- #include?<cmath>??
- #include?<queue>??
- #include?<cstdlib>??
- using?namespace?std;??
- #define?N?100010??
- typedef?long?long?ll;??
- ??
- void?manacher(string&?str)?{??
- ????int?*p=new?int[str.size()+1];??
- ????memset(p,0,sizeof(p));??
- ????int?mx=0,id=0;??
- ????for(int?i=1;?i<str.size()-1;?i++)?{??
- ????????if(mx>i)?{??
- ????????????p[i]=min(p[2*id-i],mx-i);??
- ????????}?else?p[i]=1;??
- ????????while(str[i-p[i]]==str[i+p[i]])??
- ????????????p[i]++;??
- ????????if(i+p[i]>mx)?{??
- ????????????mx=i+p[i];??
- ????????????id=i;??
- ????????}??
- ??
- ????}??
- ????int?max=0,ii;??
- ????for(int?i=1;?i<str.size();?i++)?{??
- ????????if(p[i]>max)?{??
- ????????????ii=i;??
- ????????????max=p[i];??
- ????????}??
- ????}??
- ????max--;??
- ????int?start=ii-max;??
- ????int?end=ii+max;??
- ????for(int?i=start;?i<=end;?i++)?{??
- ????????if(str[i]!='#')?{??
- ????????????cout<<str[i];??
- ????????}??
- ????}??
- ????cout<<endl;??
- ??
- ????delete?p;??
- }??
- int?main()?{??
- #ifndef?ONLINE_JUDGE??
- ????freopen("in.txt","r",stdin);??
- #endif??
- ????string?str="12213553132123";??
- ????string?s;??
- ????s+="$#";??
- ????for(int?i=0;?i<str.size();?i++)?{??
- ????????s+=str[i];??
- ????????s+="#";??
- ????}??
- ????s+="*";??
- ????cout<<s<<endl;??
- ????manacher(s);??
- ????return?0;??
- }??
最長回文
Time Limit: 4000/2000 MS (Java/Others)????Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 17168????Accepted Submission(s): 6306
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
aaaaabab
Sample Output
4 3
[cpp]?view plain?copy ?
- #include<stdio.h>??
- #include<string.h>??
- #include?<iostream>??
- #include?<string>??
- #include?<cmath>??
- #include?<queue>??
- #include?<cstdlib>??
- using?namespace?std;??
- #define?N?2000000??
- typedef?long?long?ll;??
- int?p[N];??
- void?manacher(string?str)?{??
- ??????
- ????memset(p,0,sizeof(p));??
- ????int?mx=0,id=0;??
- ????for(int?i=1;?i<str.size()-1;?i++)?{??
- ????????if(mx>i)?{??
- ????????????p[i]=min(p[2*id-i],mx-i);??
- ????????}?else?p[i]=1;??
- ????????while(str[i-p[i]]==str[i+p[i]])??
- ????????????p[i]++;??
- ????????if(i+p[i]>mx)?{??
- ????????????mx=i+p[i];??
- ????????????id=i;??
- ????????}??
- ??
- ????}??
- ????int?max=0,ii;??
- ????for(int?i=1;?i<str.size();?i++)?{??
- ????????if(p[i]>max)?{??
- ????????????ii=i;??
- ????????????max=p[i];??
- ????????}??
- ????}??
- ????max--;??
- ????/*int?start=ii-max;?
- ????int?end=ii+max;?
- ????for(int?i=start;?i<=end;?i++)?{?
- ????????if(str[i]!='#')?{?
- ????????????cout<<str[i];?
- ????????}?
- ????}*/??
- ????cout<<max<<endl;??
- }??
- int?main()?{??
- #ifndef?ONLINE_JUDGE??
- ????freopen("in.txt","r",stdin);??
- #endif??
- ????string?s,str;??
- ????while(cin>>s)?{??
- ????????//cout<<s<<endl;??
- ????????str="$#";??
- ????????for(int?i=0;?i<s.size();?i++)?{??
- ????????????str+=s[i];??
- ????????????str+="#";??
- ????????}??
- ????????str+="*";??
- ????????//cout<<str<<endl;??
- ????????manacher(str);??
- ????????getchar();??
- ????}??
- ????return?0;??
- }??
不要輕易在代碼中使用動態分配,按照原先代碼使用動態分配Wa了,也不知為什么
參考連接:
點擊打開鏈接
總結
以上是生活随笔為你收集整理的manacher算法----O(n)最长回文串的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 合肥半隐形牙套的价格?
- 下一篇: usaco Calf Flac(O(n)