今天同学问我求字符串中最大回文串的问题,最先想到的就是暴力了,时间复杂度为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);
这个代码就是为了避免很多没必要的重复匹配。这个就利用了回文串的对称性。
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]++;继续匹配后面的。