manacher算法详解

今天同学问我求字符串中最大回文串的问题,最先想到的就是暴力了,时间复杂度为O(n*n),而且还要考虑回文串长

为奇偶两种情况。后来同学说网上有O(n)的解法,就搜了下,果然manacher是在O(n)的时间复杂度里解决的。但是

面那个求数组p的过程还是不怎么理解,特在此发表希望得到论坛朋友们的指教。下面是实现代码:

#include<iostream>
#include<cstring>
using namespace std;

int manacher (char* s, int len)
{
	int nlen = 2 * len + 3;
	char* str = new char[nlen];
	int i = 0;
	int max = 0;
	str[0] = '$';
	str[1] = '#';
	for (; i < len; i++)
	{
		str[i * 2 + 2] = s[i];
		str[i * 2 + 3] = '#';
	}
	str[nlen - 1] = 0;
	int* p = new int[nlen];

	for (int i = 1; i < nlen; i++)
	{
		p[i] = 0;
	}

	int id = 0;
	for (i = 1; i < nlen; i++)
	{
		if (max > i)
			p[i] = min(p[2*id-i], p[id]+id-i);
		else
			p[i] = 1;
		while (str[i+p[i]] == str[i-p[i]])
			p[i]++;
		if (p[i]+i > max)
		{
			max = p[i] + i;
			id = i;
		}
	}

	int mx = 0;
	for (i = 1; i < nlen; i++)
	{
		if (mx < p[i] - 1)
			mx = p[i] - 1;
	}
	return mx;
}

int main()
{
	char ch[100];
	while (cin >> ch)
	{
		cout << manacher(ch, strlen(ch)) << endl;
	}
	return 0;
}



补:后来又网上找了好久,终于找到一个写的比较符合我的习惯的了。看完终于理解了这个算法。这个算法的核心就

是P的求解了,也就是

if (max > i)
    p[i] = min(p[2*id-i], p[id]+id-i);
else
    p[i] = 1;
这里max表示的是扫面str[i]后,匹配到的最远的位置。而id则记录取这个max的i值。

if (max > i)
    p[i] = min(p[2*id-i], p[id]+id-i);
这个代码就是为了避免很多没必要的重复匹配。这个就利用了回文串的对称性。

manacher算法详解_第1张图片

i点关于id点的对称点j = 2*id - i。根据对称性,p[j]的回文串也是可以对称到i这边的,但是如果p[j]的回文串对称过来以

后超过max的话,超出部分就不能对称过来,因为超出的部分是我们还没有匹配的。所以我们这里p[i]的值应该取两者

中的较小者:p[i] = min(p[2*id-i], max-i)。然后通过while (str[i+p[i]] == str[i-p[i]])  p[i]++;继续匹配后面的。




你可能感兴趣的:(manacher算法详解)