此算法主要是求一个字符串中最长回文字符串的长度。
此算法中会设置一个最右边界值R(R是右边界值再加1,即[L,R-1]为回文字符串),最长回文中心C,每个回文字符串的半径(半径不仅包括中心右边的,也包括中心)
为了能同时处理偶数,奇数回文的情况,在每个字符中插入一个额外字符作为虚轴(任何字符都行,与原字符一样都可),例如:"abcdef" ---> "#a#b#c#d#e#f"。最终结果只需要除2就可以
从左至右依次遍历,每次都会更新上述三个值。
会出现以下两种情况:
1.遍历的点i在最长回文范围外(i>=R),这个时候只能由1开始暴力扩充
2.遍历的点i在最长回文范围内,找到i在此回文范围内以C作为中心的对称点i'(此点一定记录过范围)
1)若i'的范围在最大范围内,则i的范围和i'一样
2)若i'的范围超过了左边界,则i的右边界为R。
3)若i‘的范围正好压在左边界上,那么i的右边界最少到R,需要从R开始继续判断扩充
int Manacher(string s)
{
string s2 = GetNewString(s);
int R = -1;//记录最大回文右边界的再右边
int C = -1;//记录最大回文的中心
int *Size = new int[s2.size()];//记录每个回文半径(半径包括中心点)
int max = INT_MIN;
for (int i = 0; i < s2.size(); i++)
{
Size[i] = i < R ? (Size[C - (i - C)] < R - i ? Size[C - (i - C)] : R - i) : 1;//先设置这个点的至少的回文半径
while (i + Size[i]< s2.size() && i - Size[i] >= 0)
{
if (s2[i + Size[i]] == s2[i - Size[i]])
Size[i]++;
else
break;
}
if (i + Size[i] > R)
{
R = i + Size[i];
C = i;
}
max = max < Size[i]?Size[i] : max;
}
return (2*(max-1)+1)/2;
}
string GetNewString(string s)
{
string res;
for (int i = 0; i < s.size(); i++)
{
res.push_back('#');
res.push_back(s[i]);
}
res.push_back('#');
return res;
}