【知识点】Manacher算法详解

Manacher算法

算法简介

Manacher 算法,即“马拉车”算法,是一种高效(O(n))的求最长回文子串的算法。相比于 KMPManacher 也许更好理解一些。

算法原理

对于传统的暴力解法,求最长回文子串的方式应该是对于每个位置 i ,向两边“扩大”以寻找回文子串,再记录答案。易证,此解法时间复杂度为 O(n2)

众所周知,每一个毒瘤算法都有一个暴力的开头。

Manacher 算法首先要对原字符串进行处理。因为回文串分为两种,“奇回文”和“偶回文”:

  1. "abcba"
  2. "abba"

首先,在每两个字符之间放入一个没有出现在原串的字符,如 # ;接着,将字符串的两边插入另外两个不同的,且没有出现在原串的字符,如 @$ 。操作结果如下:

  1. "@a#b#b#a$"
  2. "@a#b#c#b#a$"

神奇的事情发生了,“奇回文”和“偶回文”都转化成了“奇回文”。同时,在判定回文串的过程中也不用判边界了,因为修改后字符串边界字符不一样。

除此之外, Manacher 算法还引入了一个数组 ppi 表示以位置 i 为中心的“最长回文半径”。对于字符串 "@#a#b#b#a#d#c#a#c#d#a#$" 如表所示:

i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
si @ # a # b # b # a # d # c # a # c # d # a # $
pi 1 1 2 1 2 3 2 1 2 1 2 1 2 1 6 1 2 1 2 1 2 1 1

显然,最终答案就是 max{pi1} 。那么 Manacher 算法就讲完了……等等, p 数组怎么求?

算法过程

推荐阅读~

首先,看下图:

引入两个变量, mxidmx 表示以 id 为中心点的最大回文子串右边界。

假设现在要求 pi ,那么就有:

if(i<mx)
p[i]=min(p[id*2-i],mx-i);

这样就可以加快速度。但是……为什么?为什么为什么???

i=id2i ,也就是说,ii 关于 id 的对称点。因为是从前往后循环, pi是已知的。下面开始分类讨论:

  • pi>mxi ,可得以 i 为中心,以 mx 的对称点 mxi 的距离为半径形成的回文字符串是存在的,并且 idmxidmx 一一对应,所以 mxi 目前可以更新到的最大回文半径;
  • pi<mxi ,也就是 i 的回文半径小于 mxi 的距离,可得 pi=pi

于是就有了你看到的反人类代码。

完整代码

int manacher(string &s_src)
{
/*
* @params s_src为源字符串,s为预处理后的字符串
*/
string s="@";
for(auto &ch: s_src)
{
s.push_back('#');
s.push_back(ch);
}
s+="#$";
int id,mx=0,res=0;
for(int i=1;i<s.size()-1;i++)
{
if(i<=mx)
p[i]=min(p[id*2-i],mx-i);
while(s[i-p[i]]==s[i+p[i]])
p[i]++;
if(i+p[i]>mx)
id=i,mx=i+p[i]-1;
res=max(res,p[i]-1);
}
return res;
}

本文作者:ExplodingKonjac

本文链接:https://www.cnblogs.com/ExplodingKonjac/p/13561740.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   ExplodingKonjac  阅读(437)  评论(0编辑  收藏  举报
编辑推荐:
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
阅读排行:
· 开发的设计和重构,为开发效率服务
· 从零开始开发一个 MCP Server!
· Ai满嘴顺口溜,想考研?浪费我几个小时
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起