最长回文子串Manacher算法

发现自己时间过久了就会忘掉 ... 写一遍在这里吧~

问题: 求一个字符串的最长回文子串. 

直观思路, 每一个回文子串都可以由"中心"和"半径"来唯一决定, 如回文串"abcdbca"的中心是c, 半径长度为3. (对于总长度为偶数的回文子串, 我们先想象其中心不是一个字符, 而是字符串中间的"间隙"; 以下讨论中, 为便于分析, 暂时只考虑奇数长度的回文串)

按这个思路, 我们已经可以解决这个问题了: 对原字符串的每个字符, 都把它当作中心来求最大半径. 求最大半径的过程就是一个试图拓展的过程, 不断比较c-r和c+r处的字符串是否相同. 

while (s[c+r] == s[c-r]) {
    r = r + 1;
}

在此基础上, 我们如果考虑到, 对于已知的回文串, 其中心的左右两边是对称的, 那么就可以发现有些字符比较是不必要的. 

如图, 对于字符串S, 在我们从左至右依次把每个位置都当作中心拓展最长回文子串的过程中, 假设绿色部分是已知的回文串, 现在考虑中心C2. 那么, 注意到以C为中心的绿色左右两边的对称的. 我们可以关注一下C2的对称点C1, 如果C1的半径是r1, C2的半径至少也是r1, 可以节省r1次比较. 

// maxCover:最右边界 maxC:最右边界对应的中心 r[]:每个点作为中心的半径
curR = 1;
if (C2 < maxCover) {
    C1 = maxC - (C2 - maxC);
    r1 = r[C1];
    curR = min(r1, maxCover - C2);
}
while (s[C2 - curR] == s[C2 + curR]) {
    curR = curR + 1;
}
curR = curR - 1;

// 维护 maxCover 等

最后, 为了避免奇偶性的讨论(也就是中心是字符还是字符之间的间隙), 以及省去对字符串边界的条件判断, 对原字符串进行一次预处理: 在每两个字符中间都插入一个原串中未出现过的特殊字符, 并在字符串开头另加一个特殊字符. 

如插入'#'和'@'将"abaa"变为"@a#b#a#a".

这就是Manacher算法. 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值