马拉车算法是用于找出一个字符串中的最长回文串。
通过使用马拉车算法可以在O(n)的时间内,列出从字符串头部开始所有的回文,主要利用的是回文字符串左右镜像对称的特征。
比如对于字符串 abbbabbbb 扩展之后变成了$#a#b#b#b#a#b#b#b#b#
扩展的意义是把不论是奇数还是偶数的字符串全部都变成奇数进行处理(不包括$)。
我们从左往右依次进行计算,然后使用id表示当前搜索到的最长回文串的中心点位置,使用mx表示该回文串的半径。
这个时候i因为小于mx,所以可以利用当前id的左右镜像的对称性。
i对称点j的计算 j=id-(i-id)=2id-i
其中p[i]表示以sNew[i]为中心的最长回文子串的半径,若p[i]=1,则该回文子串就是sNew[i]本身。
p[j]=p[2*10-14]=p[6]=5;//这个时候mx-i=17-14=3<5 说明当前最大回文串的对称性只能管到mx之后的对称性就得一一重新匹配了。
如果mx-i>p[j] 这是i点的回文特性就和j的全完一样了,因为id的回文的对称性把它给包括了。
如果mx<i,说明当前点没法利用之前最长回文串的对称特征了只能一个一个匹配。
string Manacher(string s)
{
string sNew = "$#";
//在最左边使用$符号,匹配的时候就肯定不会有对称的出现所以可以防止地址越界
for (auto iter = s.cbegin(); iter != s.cend(); iter++)
{
sNew += *iter;
sNew += "#";
}
int iNewSize = sNew.size();
int iMaxSubStringLength = -1; // 最长回文子串的长度
int iMaxSubStringPos = -1; // 最长回文子串中心点的位置
vector<int> p(iNewSize, 0);
int id = 0;
int mx = 0;
for (int i = 1; i < iNewSize; i++)
{
if (i < mx)
{
p[i] = min(p[2 * id - i], mx - i);
}
else
{
p[i] = 1;
}
while (sNew[i - p[i]] == sNew[i + p[i]])
{
p[i]++;
}
if (mx < i + p[i]) //找出当前最长的回文串
{
id = i;
mx = i + p[i];
}
if (p[i] - 1 > iMaxSubStringLength)
{
iMaxSubStringLength = p[i] - 1;
iMaxSubStringPos = i;
}
}
auto iStart = s.cbegin() + (iMaxSubStringPos - iMaxSubStringLength - 1) / 2;
// 将最长回文子串起始位置转换回原串
return string(iStart, iStart + iMaxSubStringLength);
}
代码出处:https://blog.csdn.net/the_star_is_at/article/details/53354958 谢谢