Manacher马拉车算法——最长回文子串

1)基础

计算字符串的最长回文字串最简单的算法就是枚举该字符串的每一个子串,并且判断这个子串是否为回文串,这个算法的时间复杂度为O(n3)的,而稍微优化的一个算法是枚举回文串的中点(动态规划),这里要分为两种情况,一种是回文串长度是奇数的情况,另一种是回文串长度是偶数的情况,枚举中点再判断是否是回文串,这样能把算法的时间复杂度降为O(n2),但数据大的话,依然让人无法满意,这时Manacher(谐音马拉车)算法横空出世,在线性时间复杂度内求出一个字符串的最长回文字串,达到了理论上的下界。但同时,此算法的应用也十分狭窄,只能解决此类问题。

2)基本思路

其实用动态规划来解决最长回文子串比暴力法好就好在利用了一些已经算过的,减少了重复计算。而马拉车算法就是进一步得再利用已经算法的部分,使得重复计算更少。

1)
因为字符串有可能是奇数个或偶数个,所以我们要统一一下以减少计算。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3)代码实现

package manacher;
public class PlalindromeString{
    //预处理字符串,在俩个字符之间加上#
    private String preHandleString(String s){
        StringBuffer sb = new StringBuffer();
        int len = s.length();
        sb.append('#');
        for(int i=0;i<len;i++){
            sb.append(s.charAt(i));
            sb.append('#');
        }
        return sb.toString();
    }
}

//寻找最长回文子串
public String findLongestPlalindromeString(String s){
    //先预处理字符串
    String str = preHandleString(s);
    //处理后的字符串长度
    int len =str.length();
    //右边界
    int rightSide=0;
    //右边界对应的回文串中心
    int rightSideCenter=0;
    //保存以每个字符为中心的回文长度一半(向下取整)
    int[] halfLenArr = new int[len];
    //记录回文中心
    int longestHalf = 0;
    for(int i=0;i<len;i++){
        //是否需要中心扩展
        boolean needCalc = true;
        //如果在右边界的覆盖之内
        if(rightSide>i){
            //计算相对rightSideCenter的对称位置
            int leftCenter = 2*rightSideCenter-i;
            //根据回文性质得到的结论
            halfLenArr[i]=halfLenArr[leftCenter];
            //如果超越了右边界,进行调整
            if(i+halfLenArr[i]>rightSide){
                halfLenArr[i]=rightSide-i;
            }
        }
        //如果根据已知条件计算得出的最长回文小于右边界,则不需要扩展了
        if(i+halfLen[leftCenterl]<rightSide){
            //直接推出结论
            needCalc=false;
        }
        //中心扩展
        if(needCalc){
            while(i-1-halfLenArr[i] >= 0 && i+1+halfLenArr[i]<len){
                if(str.charAt(i+1+halfLenArr[i]) == str.charAt(i-1-halfLenArr[i])){
                    halfLenArr[i]++;
                }else{
                    break;
                }
            }
            //更新右边界及中心
            rightSide = i+halfLenArr[i];
            rightSideCenter = i;
            //记录最长回文串
            if(halfLenArr[i]>longestHalf){
                center= i;
                longestHalf=halfLenArr[i];
            }
        }
    }
    //去掉之前添加的#
    StringBuffer sb = new StringBuffer();
    for(int i = center-longestHalf +1;i<+center+longestHalf;i+=2){
            sb.append(str.charAt(i));
    }
    return sb.toString();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值