马拉车算法(Manachers Algorithm)个人小结

最近在刷LeetCode的时候遇到了Manacher’算法,这里来总结一下我对Maracher’s算法的理解

1. 算法由来

Maracher’s算法是为了解决最长回文子串的一种方法,这种方法将回文子串的时间复杂度降低到了(O(n))

2.预处理

为了减少处理奇回文和偶回文的麻烦,所以对原始字符串进行了处理。 例如:

abab //处理前的字符串
a#b#a#b//处理后的字符串

这样就避免了奇回文和偶回文的麻烦。

3.计算p数组

在这里,我们提出p数组的概念,p数组保存的是该位置的最长回文子串的半径。
接下来的问题是如何计算p数组。
答案很简单,从该位置开始向两边延伸。如果该位置两边的元素相等,则回文长度+1。这与求最长回文字符串的普通方法相同。
这里先提出两个概念,mx,目前通过回文子串计算能延伸到的
最远半径的最右位置以及id,及能达到最右位置的点

//最长回文字符串的中心位置索引
int index = 0;
for(int j = 1; j<n-1; j++) {
    p[j] = mx > j ? Math.min(p[2 * id - j], mx - j) : 1;
    //向左右两边延伸,扩展右边界
    while (t.charAt(j + p[j]) == t.charAt(j - p[j])) {
        p[j]++;
    }
    if(mx < p[j]+j){
        mx = p[j]+j;
        id = j;
    }
}

但是不同之处在于,当求以点j为中心的回文半径时,如果在当前的最右位置内,可以通过对称的方式求得回文半径,不再需要重复的从开头开始。

同时,我们需要考虑边界情况。当越界的情况出现时,此时需要比较p[i] = Math.min(p[2*id - i], mx - i)mx-j的大小。

如果实在不懂建议手动模拟,手动模拟是理解算法最好的方式。

4. 计算最长回文子串长度

我们得到最长回文半径和最长回文子串长度之间的关系:int maxLength = p[i]-1。maxLength表示最长回文子串长度。

5.计算最长回文字符串的起始索引

了解了最长回文字符串的长度我们还需要知道它的起始索引值,这样才能截取出最完整的最长回文子串。由于偶回文会下标越界,因此需要对其处理保证其无论如何都是奇回文,解决方法很简单,在前面加一个$即可。这样就不会变成奇回文。同时,为了满足右边界条件,也要在右边加上一个@。
最后得到公式int index = (i - p[i])/2。

贴上完整代码结束

public static String Manachers(String s){
    if(s.length()<2){
        return s;
    }
    String t= "$";
    for(int i = 0; i<s.length(); i++){
        t+= "#"+s.charAt(i);
    }
    t += "#@";
    //第二部,计算数组p
    int n = t.length();
    int[] p = new int[n];
    int id = 0, mx = 0;
    //最长回文字符串的长度
    int maxLength = -1;
    //最长回文字符串的中心位置索引
    int index = 0;
    for(int j = 1; j<n-1; j++) {
        p[j] = mx > j ? Math.min(p[2 * id - j], mx - j) : 1;
        //向左右两边延伸,扩展右边界
        while (t.charAt(j + p[j]) == t.charAt(j - p[j])) {
            p[j]++;
        }
        if(mx < p[j]+j){
            mx = p[j]+j;
            id = j;
        }
    }
    //第三部,截取字符串,输出结果
    int start = (index - maxLength)/2;
    return s.substring(start, start+maxLength);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值