在处理回文串中,奇数回文和偶数回文是一个神烦的问题,而Manacher算法巧妙地解决了这个问题。
首先,将原串进行下述处理:
1. 首尾添加分隔符,如 '#'
2. 每两个字符间添加分隔符
3.经过1 2步后,在字符串的首尾添加结束符(任意符号,加以区别,而且首尾符号不同)
例如:
原串 abcba
新串 $#a#b#c#b#a#@
这样所有的情况都变成了奇数回文串的情况。
接着用一个辅助数组p[]来存以新串的每个字符为回文中心,构成回文串的最大半径,初始为1。
如:
下标 0 1 2 3 4 5 6 7 8 9 10 11 12
新串 $ # a # b # c # b # a # @
p[ ] 1 2 1 2 1 6 1 2 1 2 1
回文串的最大长度就是p[i] - 1的最大值。
核心代码:
for(int i = 1; str2[i]; i++)//id是回文中心,Maxid是以id为回文中心,p[id]为回文半径,回文范围最远的位置
{
if(Maxid > i)
p[i] = min(p[(id << 1) - i], Maxid - i);*
else
p[i] = 1;
while(str2[p[i] + i] == str2[i - p[i]])
p[i]++;
if(p[i] + i > Maxid)
{
Maxid = p[i] + i;
id = i;
}
}
*代码解释: p[i] = min(p[(id << 1) - i], Maxid - i);
根据回文串的对称性,在回文串中,在Maxid范围内时,对称两点的对称性也是相同的,但是当超出Maxid范围时,对称性就不一定了,所以对称半径最多只到Maxid。
如图:
例题:
hdoj : http://acm.hdu.edu.cn/showproblem.php?pid=3068
AC代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M = 11e4 + 20;
char str1[M], str2[M << 1];
int p[M << 1];
main()
{
while(~scanf("%s", &str1[1]))//从1开始
{
int Maxid = 0, id = 0, ans = 0;
str1[0] = '#';//覆盖原来的'\0'
for(int i = 1; str1[i]; i++)
{
str2[i << 1] = str1[i];
str2[(i << 1) + 1] = '#';
}
str2[0] = '$';
str2[1] = '#';
str2[(strlen(str1) << 1) + 2] = '\0';
for(int i = 1; str2[i]; i++)
{
if(Maxid > i)
p[i] = min(p[(id << 1) - i], Maxid - i);
else
p[i] = 1;
while(str2[p[i] + i] == str2[i - p[i]])
p[i]++;
if(p[i] + i > Maxid)
{
Maxid = p[i] + i;
id = i;
}
if(p[i] > ans)
ans = p[i];
}
printf("%d\n", ans - 1);
}
}
例题:
hdoj : http://acm.hdu.edu.cn/showproblem.php?pid=4513
思路:
这题是在manacher上的变形,关键是上升序列的处理方式。看代码比较直观。
#include<cstdio>
#include<algorithm>
using namespace std;
const int M = 1e5 + 20;
int human[M * 2], p[M * 2];
main()
{
int n, Tcase;
scanf("%d", &Tcase);
while(Tcase--)
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
int a;
scanf("%d", &a);
human[i * 2] = a;
human[i * 2 + 1] = -3;
}
human[n * 2 + 2] = -1;
human[1] = -3;
human[0] = -2;
int maxid = 0, ans = 0, id = 0;
for(int i = 1; i < n * 2 + 2; i++)
{
if(maxid > i)
p[i] = min(p[id * 2 - i], maxid - i);
else
p[i] = 1;
int s;
if(human[i] == -3)
s = human[i + 1];
else
s = human[i];
while(human[i - p[i]] == human[i + p[i]])
{
if(human[i - p[i]] == -3)
p[i]++;
else if(human[i + p[i]] <= s)
{
s = human[i + p[i]];
p[i]++;
}
else
{
break;
}
}
if(p[i] + i > maxid)
{
maxid = p[i] + i;
id = i;
}
if(p[i] > ans)
ans = p[i];
}
printf("%d\n", ans - 1);
}
}
/*
50
3
51 52 51
4
51 52 52 51
5
50 51 52 51 50
5
52 51 50 51 52
*/