最长回文子串(Longest Palindrome Substring)--Manacher算法O(n)

来源:互联网 发布:民营经济增加值数据 编辑:程序博客网 时间:2024/06/06 01:34

Problem Description:

给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.

回文就是正反读都是一样的字符串,如aba, abba等


下面介绍一种复杂度只有O(n)的Manacher算法:

首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个特殊的符号。比如 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的对应关系:



可以看出,P[i]-1正好是原字符串中回文串的总长度,可证明。

那么怎么计算P[i]呢?该算法增加两个辅助变量id和mx,其中id表示最大回文子串中心的位置,mx则为id+P[id],表示i之前的回文串中延伸至最右端的位置也就是最大回文子串的边界。

for(int i = 1; i < len; ++i){if(mx > i)P[i] = MIN(P[id*2-i] , mx-i);elseP[i] = 1;while(S[i+P[i]] == S[i-P[i]])++P[i];if(i + P[i] > mx){mx = i + P[i];id = i;}}

算法的关键点在这里:如果mx>i,那么P[i] >= MIN(P[2*id-i], mx-i)。


代码:

int longestPalindromic(string str){int strsz = str.size();if(strsz<=1)return strsz;int len = strsz*2+2;int* s = new int[len+1];s[0]='$';for(int i=1,j=0; j<strsz; j++){s[i++] = '#';s[i++] = str[j];}s[len-1]='#';s[len]='\0';int id = 0;int mx = 0;int longestnum = 1;int longestid = 0;int* p = new int[len];for(int i=1; i<len; i++){if(mx>i)p[i]=min(p[id*2-i],mx-i);elsep[i]=1;while(s[i+p[i]] == s[i-p[i]])p[i]++;if(p[i]+i>mx){mx=p[i]+i;id=i;}if(p[i]>longestnum){longestnum=p[i];longestid = i;}}delete[] p;delete[] s;cout<<"Longest Palindromic Num:"<<longestnum-1<<endl;cout<<str.substr((longestid-1)>>1-(longestnum-1)>>1,longestnum-1)<<endl;return longestnum-1;}

其实这题也可以用后缀数组来做,但是后缀数组的复杂度太高:翻转拼接(O(n)),排序(O(nlogn)),找最长的串(O(2n*len)),找的时候还要注意一个相邻的两个串一个有#一个没有#。




参考文章:

http://blog.csdn.net/ggggiqnypgjg/article/details/6645824
http://zhuhongcheng.wordpress.com/2009/08/02/a-simple-linear-time-algorithm-for-finding-longest-palindrome-sub-string/

原创粉丝点击