Manacher 学习笔记

Manacher 学习笔记

一、引入

首先我们需要知道的是 Manacher 是解决回文串问题的有效工具。

一个通用的问题模型是给定一个长度为 n 的字符串 s,统计该字符串中所有的回文子串的个数。Manacher 算法可以在 O(n) 的时间复杂度内解决这个问题。

我们知道的是,对于任意一个回文串 s[i,j],有对称中心 i+j12。考虑到奇数和偶数长度回文串的差异,我们用两个数组 d1(i),d2(i) 分别表示长度为奇数、偶数的回文串中心为 i 时回文串的个数。

现在我们的问题就是快速求出 d1,d2 两个数组。

二、算法

我们先考虑 d1 的求解过程,求出 d1 数组的基本思想是采用之前维护过的 d1 数组信息来更新现有的 d1 数组。为了叙述方便,下文以 d 数组代指 d1 数组。

既然这样,在求 d(i) 时我们需要维护 [1,i1] 范围内边界最靠右的回文串的边界 l,r,初始设 l=1,r=0

现在考虑计算 d(i) 的方式:

  • i>r,暴力匹配;
  • ir

由于 [l,r] 是回文串,那么 d(i)=d(l+ri),也就是将中心为 l+ri 为中心的回文串 copy 到了 i 上。然而需要留意的是,我们只能保证在 [l,r] 内的部分是回文串,而这个部份内回文串的最大长度显然是 ri+1

考虑复杂度分析:r 是单调递增的,而暴力算法运行一次一定会使得 r 增长 1,因此复杂度为 O(n)

现在考虑 d2(i) 的求解——但其实我们有更好的方法用一套模板解决两个问题。

考虑在原字符串每个字符的两侧都插入一个其它字符,这样一来所有回文串都可以归约到奇数串的情况,例如 aba a#b#aabba a#b#b#a。对于多统计的情形,容易得到关系 d(i)=2×d1(i)=2×d2(i)+1

给出代码:

void manacher() {
	int l = 1, r = 0;
	for (int i = 1; i <= n; i++) {
		int k = (i > r) ? 0 : min(d[l + r - i], r - i + 1);
		while (i + k <= n && i - k >= 1 && equ(s[i - k], s[i + k])) ++k;
		d[i] = k--;
		if (i + k > r) l = i - k, r = i + k;
	}
}

三、应用

应用典型是求最长回文子串。对于长度为奇数的回文串,最长的长度是 2×d11=d1,偶数是 2×d2=d1。于是返回 maxd1 即可。

posted @   长安19路  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示