36.Manacher 算法
一、基本介绍
本节我们将介绍一种算法来解决一类关于回文字符串的问题。给定一个长度为 nnn 的字符串 sss,请找到所有使得子串 s[i…j]s[i \dots j]s[i…j] 为一个回文串的二元组 (i,j)(i, j)(i,j)。当 s=srevs=s_{\text{rev}}s=srev 时,我们说字符串 sss 是一个回文串,srevs_{\text{rev}}srev 被定义为 sss 的反转字符串。
本节所要讲的算法名叫 Manacher 算法,它是一个能够在线性时间内得到一个字符串中最长回文子串的算法,性能比其他的算法,如字符串哈希还要优秀,且不会被特殊数据卡住。Manacher 算法的核心思想和 KMP 算法非常相似,都是利用已经计算过的信息来加快字符串的匹配进程。目前 Manacher 算法是处理回文串最有效的工具之一。
二、算法分析
我们可以知道在最坏情况下,例如对于字符串 s=aaaa...as=aaaa...as=aaaa...a 来说有 n2n^2n2 个回文串,因此该问题似乎并无线性算法。首先我们面临一个问题,那就是我们如何处理奇数回文串和偶数回文串这两种不同情况。我们可以采取如下处理方式:
- 假设存在一个长度为奇数的字符串 str=abbabbastr=abbabbastr=abbabba
- 我们可以将其转化为一个新的字符串 s=∗@a@b@b@a@b@b@a@!s=*@a@b@b@a@b@b@a@!s=∗@a@b@b@a@b@b@a@!
- 原则是首尾两个字符不同,和中间的字符也不同(防止出现访问越界),中间所有字符相同。
1.求解最终答案
为了引出 Manacher 算法 我们下面给出一种关于回文串的信息的更紧凑表达方式:对于每个位置 i=0…n−1i=0 \dots n-1i=0…n−1,我们找出值 d[i]d[i]d[i]。d[i]d[i]d[i] 表示以位置 iii 为中心的回文串个数。换个角度,二者也表示了以位置 iii 为中心的最长回文串的半径长度(半径长度 d[i]d[i]d[i] 为从位置 iii 到回文串最右端位置包含的字符个数。与此同时 d[i]d[i]d[i] 也可以表示以 iii 为中心的最长回文串的长度。
我们首先考虑下面我们给出一个结论,ans=d[i]−1ans=d[i]-1ans=d[i]−1,我们在这里以举例的方式简单说明,现在我们来观察一下这个新字符串 sss:
- 以我们的 s[8]=as[8]=as[8]=a 为中心的回文串向右最大拓展长度 +1+1+1 即为 d[8]=8d[8]=8d[8]=8,而回到原字符串,我们知道以 str[3]str[3]str[3] 为中心的最大回文字串长度为 7=d[8]−17=d[8]-17=d[8]−1。
- 以我们的 s[5]=@s[5]=@s[5]=@ 为中心的回文串向右最大拓展长度 +1+1+1 即为 d[5]=5d[5]=5d[5]=5,而回到原字符串,我们知道以 str[1,2]str[1,2]str[1,2] 为中心的最大回文字串长度为 4=d[5]−14=d[5]-14=d[5]−1。
- 由此可以得到结论,ans=max(d[i]−1)ans=\max(d[i]-1)ans=max(d[i]−1)。
2.求解数组 d[i]d[i]d[i]
下面我们来介绍应该如何求解数组 d[i]d[i]d[i],也就是 Manacher 算法的核心部分。假设此时我们已经得到 d[0]∼d[i−1]d[0]\sim d[i-1]d[0]∼d[i−1],那么现在来求 d[i]d[i]d[i]。
现在我们结合下图来做具体分析:
-
mxmxmx 表示前面的所有结果中向右扩展得最多的 ddd ,ididid 表示 mxmxmx 对应的中心字符位置。
-
因为 i≥mxi\geq mxi≥mx 时很简单,我们只需要暴力拓展即可,因为后面的都是未计算内容。下面我们只分析 i<mxi<mxi<mx 的情况。
-
我们可以找到 iii 关于 ididid 的对称点 i′=2⋅id−ii'=2\cdot id-ii′=2⋅id−i,而 d[i′]d[i']d[i′] 我们是知道的(即绿色弧线部分),根据对称性,理想的回文即为绿色部分。
-
如果 i+d[i′]>mxi+d[i']>mxi+d[i′]>mx,那么 d[i]=mx−id[i]=mx-id[i]=mx−i。因为超过的部分(即红色区块)根据关于 ididid 的对称性可知道是以 ididid 为中心的回文串没有拓展到的位置,因为 mxmxmx 就是最大拓展,即说明不对称。而根据定义在 mxmxmx 以内是满足对称的(即蓝色区块),所以 d[i]=mx−id[i]=mx-id[i]=mx−i。
-
而当 i+d[i′]≤mxi+d[i'] \leq mxi+d[i′]≤mx 时,显然这时候以 ididid 为中心,mxmxmx 为半径的字串是对称的。i+d[i′]i+d[i']i+d[i′] 被包含在这个区间内,而 i+d[i]i+d[i]i+d[i] 和 mxmxmx 中间部分无法计入答案的原因是,根据对称性,i′i'i′ 无法匹配到,那么iii也无法匹配到。所以 d[i]=d[i′]d[i]=d[i']d[i]=d[i′]。
所以综上所述,在 i<mxi<mxi<mx 时,d[i]=min(mx−i,d[2⋅id−i])d[i]=\min(mx-i,d[2\cdot id-i])d[i]=min(mx−i,d[2⋅id−i])。下面是核心代码:
d[0]=0;
ll mx = 0, ans = 0, id = 0;
for(ll i = 1; i < count1; i++)
{if(i < mx)d[i] = min(mx - i, d[2 * id - i]);elsed[i] = 1;while(s[i - d[i]] == s[i + d[i]])d[i]++;if(i + d[i] > mx){mx = d[i] + i;id = i;ans = max(ans, d[i] - 1);}
}
三、作业
1.绿题
UVA11475 Extend to Palindrome
2.蓝题
P1659 [国家集训队] 拉拉队排练
P3501 [POI 2010] ANT-Antisymmetry