适用场景
专用于快速查找字符中的最长回文
细节
在回文问题中,我们有时会使用中心扩散的方式进行求解,但直接使用中心扩散的方式.需要考虑回文为偶数个以及奇数个的情况,增加了算法的复杂度
在Manacher算法中,使用了较为巧妙的方式进行处理数据,使所有的回文变为奇回文
- 在字符串前后使用不同的字符用做标记字符串的开始以及结束, 且该字符不会在目标字符串中出现.目的是为了在进行中心扩散时,无需考虑边界
- 所有字符之间使用另一个特殊字符进行间隔(与作为开始结束的字符也不同)
例如 kabakdkabacdc 处理成 ^#k#a#b#a#k#d#k#a#b#a#c#d#c#$
| index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 1 0 | 1 1 | 1 2 | 1 3 | 1 4 | 1 5 | 
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 处理后的字符串 | ^ | # | k | # | a | # | b | # | a | # | k | # | d | # | k | # | 
| 1 6 | 1 7 | 1 8 | 1 9 | 2 0 | 2 1 | 2 2 | 2 3 | 2 4 | 2 5 | 2 6 | 2 7 | 2 8 | ||||
| a | # | b | # | a | # | c | # | d | # | c | # | $ | 
除此之外还使用了回文对称的特点对查询的过程进行加速,将已知的回文长度存储服务于后续的回文长度统计
PS: 存储时可以记录当前点为中心的整个回文长度,或者是记录半个长度(包含中点或者不包含),此处我是不包含中点的半个长度
那么表格将会变成这样(^#$ 都为0)
| index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 1 0 | 1 1 | 1 2 | 1 3 | 1 4 | 1 5 | 
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 处理后的字符串 | ^ | # | k | # | a | # | b | # | a | # | k | # | d | # | k | # | 
| 不包含中点的回文半长 | ^ | # | 1 | # | 1 | # | 5 | # | 1 | # | 1 | # | 9 | # | 1 | # | 
| 1 6 | 1 7 | 1 8 | 1 9 | 2 0 | 2 1 | 2 2 | 2 3 | 2 4 | 2 5 | 2 6 | 2 7 | 2 8 | ||||
| a | # | b | # | a | # | c | # | d | # | c | # | $ | ||||
| 1 | # | 9 | # | 1 | # | 1 | # | 3 | # | 1 | # | $ | 
那么是怎么使用已有的信息进行加速呢
假设当前我们已经遍历到了第14位,那我们其实已经知道了以第12位的d为中心的最大回文半长是9,第14位的k包含在这个半径之内
所以一定能以第12位的d为对称轴,查到第12位的k的对称点第10位的k,因为第10位的k与第12位的d的距离加上自身的最大回文半径未超过第12位的d的回文半径,所以第14位的k能够直接使用第12位的k已计算的半径,然后再继续使用中心扩展进行尝试是否还能扩大以第14位的k为中心的回文范围
假设当我们遍历到第18位的b,还是找对称点第6位的b,因为第12位的d与第12位的d的距离加上自身的最大回文半径超过第12位的d的回文半径,所以第18位的b只能获取第6位的b未超出部分长度,虽然第6位的b最大回文半径是5,但能映射的最大值是3.因为d只能保证自身回文范围内的是对称的.
接下来第18位的b就能在这个范围的基础上继续中心扩展,最终确定最大回文范围
在算法的实现过程中,我们常常会缓存对称点加上半径的长度,所以也可以这样计算
所以可以直接同时取和计算未超出部分,取最小值即可.1
当前需要计算点的初始回文半径 = min(映射位的最大回文半径, 未超出对称轴最大回文半径的长度)
实现
| 1 | impl Solution { |