最长回文子串Manacher算法

[亡魂溺海] 提交于 2019-12-04 20:17:48

回文串是这样的abba,ababa,就是把这个串翻转过来和原串是一样的, 最长回文子串,就是在一个长串中找到一个 子串,这个子串是 长串中的最长回文子串

简单的做法是 指定前后两个指针,判断这两个指针之间的字符串是否是回文串,并记录最大值

有一个算法是来计算最长回文子串的叫做Manacher,在网上找了半天有点没看懂,最后自己在纸上模拟一下才弄懂,说一下自己的思路


首先将字符串S用‘#’分隔,以"waawabawab" 为例子

原串S变成

S:   # w # a # a # w # a # b # a # w # a # b #


然后我们用一个p数组,p[i]记录以str[i]为中间字符的回文串向右能匹配的长度 我们人工计算一下可以看到结果是这样的

S:    0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
i:    #  w  #  a  #  a  #  w  #  a  #  b  #  a  #  w  #  a  #  b  #
p[i]: 1  2  1  2  5  2  1  4  1  2  1  8  1  2  1  6  1  2  1  2  1

可以看到 当i=4时,当前字符是‘#’,以它为中间字符能构成的最长回文串是#w#a(#)a#w# ,括号内为当前字符,所以结果是5,但是这个值用程序怎么做出呢,还要以它为中间字符向左右遍历查找吗,当然不是。。。

我们引入两个变量id和mx, mx记录的是能够影响到的最右边的位置, 并且mx是以S[id]为中间字符的

当位置是0时,id=0,mx=0

当位置是1时,id=1,mx=2

当位置是2时,id=1,mx=2

当位置是3时,id=3,mx=4

当位置是4时,id=4,mx=8

当位置是5时,id=4,mx=8

...

当位置是11时,id=11,mx=18

...

当位置是15时,id=11,mx=18

我们就以这个位置为15的字符w为例子,假设前面的p[i]都已经算出来了,该怎么算出这个位置的p值呢,可以看到当前字符是在 以S[id]为中间字符的最长回文串里,那么以id=11做一个对称,位置为7的也应该w

而且p[15] 应该 等于 p[7]。。。7是这样算出来的,当前位置是i,以id做对称,那么位置应该是 2*id - i

这里有一个问题,我们先让p[i] = p[2*id-i] ,那么如果 以S[2*id-1]为中间字符的最长回文子串 最左端  已经超过了  以以S[id]为中间字符的最长回文子串最左端, 这就出现了问题,所以我们应该让

p[i] = min(p[2*id-1],mx-i+1);   // mx-i+1 是能影响到的最右端的位置

然后我们在向后遍历 

while(i-p[i]>=0&&S[i+p[i]] == S[i-p[i]]) {
    p[i]++;
}
这样就能得到正确的结果了,并且需要更新id和mx值

代码是leetcode 第五题

char* longestPalindrome(char* s) {
	int lens = strlen(s);
	char as[100000];
	int p[100000];
	int i=0,j=0;
	// add # into s
	as[j++] = '#';
	for(i=0; s[i]; i++){
		as[j++]=s[i];
		as[j++]='#';
	}
	as[j] = 0;
	int id,mx=0;
	int result=0,resultId;
	p[0]=1;
	for(i=1; as[i]; i++){
		p[i] = 1;
		if(mx > i){
			p[i] = p[2*id-i];
			if(p[i] > mx-i+1) p[i] = mx-i+1;
		}
		while(i-p[i]>=0&&as[i+p[i]] == as[i-p[i]]) {
			p[i]++;
		}
		if(i+p[i]-1 > mx) {
			mx = i+p[i]-1;
			id = i;
		}
		if(p[i]>result) {
			result = p[i];
			resultId = i;
		}

	}
	int left = (resultId - result +1);
	int right = (resultId + result -1);
	if(as[left]=='#') left++;
	if(as[right]=='#') right--;
	left/=2; right/=2;
	s[right+1]=0;
	return s+left;
}


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!