[leetcode 5] Longest Palindromic Substring

O(n)求最长回文子串
本文介绍一种在O(n)时间内找到给定字符串中最长回文子串的方法。利用折叠特性,通过计算每个可能中心的回文半径,并进行预处理以确保每个回文串都有奇数个字符,最终只需遍历一次字符串即可解决问题。

题目:

Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
思路:

1.本题目可以在O(n)时间复杂度内完成;

2.本算法采取折叠的思路,对于一个回文字符串来说,可以以中间字符进行折叠,折叠后前段字符串和后段字符串可以完全重合;

3.如图所示:
4.在此图中,设c为一个回文字符串的中心,R为该回文字符串的右边界,L为其左边界,对于任何一点j来说,若想计算以j为中心的回文字符串的右边界,j关于c的对称点i具有重要的参考意义,以一个数组p存储以任何一点k为中心的回文字符串的半径(即其右边界至该点的距离),那么p[j]就是以j为中心的回文字符串的半径;

5.现在考虑以i为中心的回文字符串,若其半径p[i]小于i到L的距离(依照回文字符串对称性,也即j到R的距离),则p[j]>=p[i](如0 4 2 3 2 4 2 3 2 4 2,以第二个4为中心的回文字符串为4 2 3 2 4 2 3 2 4,以第一个3为中心的回文字符串为4 2 3 2 4,第二个3与第一个3根据第二个4对称,以第二个3为中心的回文字符串为2 4 2 3 2 4 2),此外,按照折叠思路,由于以c为中心折叠,[L,C]与[C,R]可以完全重合,那么在[L,R]范围内,[i-p[i],i+[pi]]与[j-[pj],j+p[j]]也可完全重合;

6.若p[i]大于R-j,那么以j为中心,以R-j为半径的字符串必然是对称的;

7.根据4、5步可以快速确定以j为圆心的回文字符串的最小半径,之后具体半径计算则需在此基础上进行再计算,但是最小半径的确定已经节省了大量时间;


8.上述图中j位于R之前,对于j位于R之后的情况,p[i]已没有任何参考意义,所以p[j]需要从0开始计算;

9.按照折叠理论,每一个回文字符串都可以一个中心进行折叠,但是若回文字符串有偶数个字符,则没有所谓的中心点,基于此,我们可以对原始字符串进行修改,以方便可以使任何一个回文字符串拥有奇数个字符,具体方法为在任意两个字符之间插入一个特殊符号(如#);


10.依照上述分析,只要不断更新当前最长回文字符串的中心点C与半径R,使用一个指针j从头遍历字符串一次即可获取字符串的最长回文字符子串,于是,该算法时间复杂度为O(n);

代码:

<span style="font-weight: normal;"><span style="font-size:14px;">string process(string s){
	int n = s.length();
	if (n == 0) return "^$";
	string ret = "^";
	for (int i = 0; i < n; i++)
		ret += "#" + s.substr(i, 1);
	ret += "#$";
	return ret;
}

class Solution{
public:
	string longestPalindrome(string s){
		s=process(s);
		int len=s.length();
		int *p=new int[len];
		int c=0;int r=0;
		for(int j=1;j!=len-1;++j)
		{
			int i=2*c-j;
			p[j]=(j>c+r)?0:min(p[i],c+r-j);
			while(s[j+p[j]+1]==s[j-p[j]-1])
				++p[j];
			if(p[j]>r)
			{
				c=j;
				r=p[j];
			}
		}
		int maxlen=0;
		int center=0;
		for(int i=0;i!=len-1;++i)
		{
			if(p[i]>maxlen)
			{
				maxlen=p[i];
				center=i;
			}
		}		
		delete[] p;
		s=s.substr(center-maxlen,2*maxlen+1);
		string result;
		for(int i=1;i<=s.length()-2;i+=2)
			result+=s[i];
		return result;
	}
};</span></span>

补充:

1.在对字符串进行预处理时,为了避免字符串越界,需要在其头部和尾部分别做一个标记,如skyhuskyykspanda,处理后为^#s#k#y#h#u#s#k#y#y#k#s#p#a#n#d#a#$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值