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

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);
		else
			P[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);
		else
			p[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/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值