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)最长回文串的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 合肥半隐形牙套的价格?
 - 下一篇: 求一个想找个男朋友的个性签名!