manacher 算法

序言

manacher 好像有点不熟练了,怕翻车了,写篇文章理理思路。


目标

给定一个字符串 S S S,以 O ( n ) O(n) O(n) 的复杂度,求出以每一个点为中心最长的回文子串的长度。

预处理

首先对于字符串如 abaccaa,将其两两字符之间插入一个#,并在首尾各插入一个不常用的字符(避免边界问题),如变为?#a#b#a#c#c#a#a#$,这样的一个新串在处理时就无需考虑长度为偶数的回文子串,如回文子串 acca 在新串中对应的 #a#c#c#a#,且新串中回文子串的半长恰为原来的两倍。(半长意为一个奇数回文子串的中心到其中一个端点的字符串数)

暴力

对于每一个字符,暴力枚举比对以其为中心的最长回文子串的长度, O ( n 2 ) O(n^2) O(n2)


manacher

manacher 利用回文串的性质,通过对不同情况的讨论处理,完成对回文子串的 O ( n ) O(n) O(n) 求解。

回文串性质

给一个回文串 S S S,如 cabaemeabac,我们发现这个的左半边包含了一个回文子串aba,那么其右半边对称的位置也一定有这样一个回文子串aba,反之亦然。

形式化: S = { a 1 a 2 . . . a n − 1 a n } S=\{a_1a_2...a_{n-1}a_n\} S={a1a2...an1an} 是回文串, ∃ i ≤ j , i ≠ n − j + 1 , { a i a i + 1 . . . a j − 1 a j } \exist i \le j,i \neq n-j + 1,\{a_ia_{i+1}...a_{j-1}a_j\} iji=nj+1{aiai+1...aj1aj} 是回文子串,那么 { a n − j + 1 a n − j + 2 . . . a n − i a n − i + 1 } \{a_{n-j+1}a_{n-j+2}...a_{n-i}a_{n-i+1}\} {anj+1anj+2...aniani+1} 也一定是回文子串。

同样的如果不是回文子串,那么对称的位置也一定不为回文子串。

三个标记和一个数组:

P:Position,当前字符所在位置;
R:Right,当前最靠右的最长回文子串的右端点所在位置;
C:Center,当前最靠右的最长回文子串的中心所在位置;
r[i]:表示以第 i i i 个字符为中心的最长回文子串长度半长,所以 i + r [ i ] i+r[i] i+r[i] 即为以第 i i i 个字符为中心的最长回文子串的右端点所在位置。

分类讨论:

  1. P > R P > R P>R r [ P ] = 1 r[P]=1 r[P]=1
  2. P ≤ R P \le R PR,找到 P P P 关于 C C C 的对称位置 D D D D = C − ( P − C ) = 2 C − P D =C-(P-C)=2C-P D=C(PC)=2CP
    2.1. P + r [ D ] ≤ R P+r[D] \le R P+r[D]R,根据性质, r [ P ] = r [ D ] r[P] = r[D] r[P]=r[D]
    2.2. P + r [ D ] > R P+r[D] > R P+r[D]>R,根据性质, r [ P ] = R − P + 1 r[P] = R-P+1 r[P]=RP+1(认真看一下性质)。

对于 1 1 1 2.2 2.2 2.2 2.1 2.1 2.1 如果暴力找显然直接结束)都还可以以 P P P 为中心寻找更长的回文子串(暴力查找),然后更新 C C C R R R

复杂度分析

复杂度的瓶颈显然在于暴力查找匹配最长回文子串的过程。

每次暴力查找都是从 R R R 的位置开始找,且每次找完都一定会向右更新 R R R,所以暴力查找次数等于 R R R 的最大值,因为 R ≤ ∣ S ∣ R \le |S| RS,暴力查找最多进行 ∣ S ∣ |S| S 次,所以复杂度为 O ( n ) O(n) O(n)


代码

void Manacher()
{
	int C = 1, R = 1;
	r[1] = 1;
	for(int P = 1; P <= n; ++P)
	{
		r[P] = min(R - P + 1, r[C * 2 - P]); //讨论了1,2.1和2.2
		while(S[P - r[P]] == S[P + r[P]]) ++r[P]; //暴力判断回文子串
		if(R < P + r[P] - 1){ R = P + r[P] - 1; C = P; } //更新C和P
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值