简单易懂的manacher算法讲解
manacher
求最长回文子串的算法(顺便还能求出来以每个点为中心的最长回文子串)
介绍
先来一道模板题:P3805 【模板】manacher 算法
先考虑一下小学二年级都会的纯暴力解法:以每个字符为中心,向左右扩展直到左右端点不相等为止,这时遍历出来的字符串长度记为这个点的答案,最后结果即为所有点答案取max
很显然,这个方法有两个问题:
- 无法处理偶数长度的回文字符串。如回文字符串ABBA,实际上不存在一个字符中心可以向外扩展,按照我们的算法,从A、B、B、A四个点扩散出去的答案均为1。当然这个问题并不致命,我们只需要两个字符中间也往外扩展即可。
- 时间复杂度
,稍微大一点的数据都过不了,直接T飞
过不去那道题?
事实:他数据很大
而暴力是
它 最 慢 了!
介绍——马拉车manacher!
首先针对第一个问题,manacher有一个非常巧妙的处理办法:
在每两个字符之间插进去一个统一的字符,比如说'#'。比如我们上文提到的"ABBA",在做完之后就变成了这样的字符串:"#A#B#B#A#",而"ABA"则变成了"#A#B#A#"。
发现什么了吗?原来长度为奇数的回文字符串中心不变,偶数的回文字符串变成了以'#'为中心的长度为奇数的回文串,而假设我们得到的回文串长度为
怎么降低复杂度呢?当然是用万能的字符串哈希了
我们仍然考虑从左往右枚举,只不过这次用
这个有什么用呢?首先考虑到我们处理到了某一个点
为什么呢?先看下面一张图
而由于黄框为最长回文串,所以黄框左右两边的字符一定不同(不然最长回文串长度还能再加),所以对称过去
当然这是黄框没有超过
很简单:直接从
首先,对于黄框没有超过
考虑黄框超过
而当
基本思想已经介绍完了,上代码
manacher
s[0]=114514; for(int i=0;i<stmp.size();i++){ s[++cnt]=stmp[i]; s[++cnt]=114514;//我这里为了更好操作用的int来存字符串,所以间隔符可以乱填,一般情况下都用的是'#' } n=cnt; for(int i=0;i<=n;i++){ if(i<R) len[i]=min(2*C-i>=0?len[2*C-i]:0,R-i-1); //当黄框不超过r时,他就等于i'的len,否则从r开始枚举。 //注意由于我len[1]设定为1所以i+len[i]实际上指到的是回文串右端点右边的点,所以要R-i-1 else len[i]=1; for(;i+len[i]<=n&&i-len[i]>=0&&s[i+len[i]]==s[i-len[i]];len[i]++); //一行的暴力向右推 if(i+len[i]>R) R=i+len[i],C=i; //更新R,C ans=max(len[i],ans); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效