Manacher‘s Algorithm 马拉车算法

本文介绍了Manacher's Algorithm,一种利用回文串对称性高效求解最长回文子串问题的算法。通过预处理将字符串转化为奇数回文串,并维护一个lens数组记录以每个字符为中心的最长回文半径。在更新过程中,考虑回文串边界情况,判断当前位置是否在已知回文串内,并据此计算当前回文半径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Longest Palindromic Substring

跟之前算法没什么区别的地方就是,还是需要两两对比确定是否为Palidrome
maintain一个叫lens的array,lens[i] - 以i为中心的最大Palindrome的半径(包括自身)
example:s="OABAXABAO" -> lens[2] = 2, 以"B"为中心的最大Palindrome半径为2
计算方法为

lens[i] = 1
for(s[i+lens[i]] == s[i-lens[i]]){
    lens[i]++;
}

区别于之前两种算法,Manacher's Algorithm将palindrome的symmetric特性用到了极致

1.基本运用 我们还是看这条string s="OABAXABAO",已知s是palindrome,左边B跟右边B关于X对称
更加神奇的是源于palidrome's symmetricity,lens[6] = lens[2] = 2 [右边B跟左边B最大Palindrome半径也相等]

2.左越界 But this is not always the case,比如说 s="XABAXABAO",lens[2] = 3,lens[6] = 2 [左边B最大Palindrome半径为3,右边B最大Palindrome半径为2] 尽管两个B还是关于X对称,但是左边B最大Palindrome半径已经超过了X 最大Palindrome的管辖范围,所以右边B最大Palindrome半径不能单纯的利用对称,应该至少是lens[6] = 4 - 2 [右边B最大Palindrome半径为 index of X 减去index of 左边B]

3.右越界 这还没完,比如说 s="OABAXABAX",lens[2] = 2,lens[6] = 3 [左边B最大Palindrome半径为2,右边B最大Palindrome半径为3] 同上,右边B最大Palindrome半径不能单纯的利用对称,应该至少是左边B最大Palindrome半径的大小。

稍稍总结一下,运用palindrome的对称性:
如果左右都不越界,右B最大Palindrome半径就是左B最大Palindrome半径
如果左越界,右B最大Palindrome半径就是至少是左B中心X的距离
如果右越界,右B最大Palindrome半径就是至少是左B最大Palindrome半径
[哈哈,但真实的世界总是给你一记耳光~] 其实遍历array的时候,根本就不能提前知道是否右越界,所以最后右B最大Palindrome半径应该至少是 min(lens[左B], 左B中心X的距离)

聪明的小伙伴可能发现了,你上面说的palidrome's symmetricity全都是奇数Palindrome啊?那偶数的怎么办啊?
来,不急~ 使用Manacher's Algorithm还需要预处理,在每个字母两边都要插入“#”,比如
"OABAXABAO" -> "#O#A#B#A#X#A#B#A#O#"
那就全变成奇数Palindrome了啊~~~ 开心?

接下来,解释code啦~

i - current index
mx - 之前出现Palindrome里右边到达的最远index (用于判断i是否在某个Palindrome里)
id - 之前出现右边到达的最远Palindrome的中心(用于得到i的对称点)
lens[i] = mx > i ? min(lens[2*id-1], mx-i) : 1;
mx > i:i是否在某个Palindrome里?
是:min(lens[2*id-1], mx-i),至少为min(对称点的最大Palindrome半径, 对称点到中心的距离)
否:1
然后用最开始提到的方法计算最终的最大Palindrome半径,update各种variables

class Solution {
public:
    string longestPalindrome(string s) {
        if(s.empty()) return "";
        string t = "$#";
        for(char c: s){
            t += c;
            t += '#';
        }
        vector<int> lens(t.size(), 0);
        int mx=0, id=0, resLen=0, resCenter=0;
        for(int i=1;i<t.size();i++){
            lens[i] = mx > i ? min(lens[2*id-i], mx-i):1;
            while(t[i+lens[i]]==t[i-lens[i]]) lens[i]++;
            if(mx<i+lens[i]){
                mx = i+lens[i];
                id = i;
            }
            if(resLen<lens[i]){
                resLen = lens[i];
                resCenter = i;
            }
        }
        return s.substr((resCenter - resLen) / 2, resLen - 1);
    }
};

Reference: 
http://www.cnblogs.com/grandyang/p/4475985.html
https://blog.crimx.com/2017/07/06/manachers-algorithm/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值