马拉车算法(Manacher‘s Algorithm)

马拉车算法(Manacher's Algorithm)是一种在O(n)时间复杂度内解决字符串回文子串问题的算法。它通过预处理字符串,利用回文串的对称性和中心扩展法,动态维护回文串的中心和半径。在求解过程中,考虑了边界和对称性的情况,更新回文串的中心和范围。最终找到最长回文子串并返回其起始下标。
摘要由CSDN通过智能技术生成

马拉车算法

算法定义:

Manachar算法主要是处理字符串中关于回文串的问题的,它的强大之处在于,可以在O(n)的时间复杂度中解决回文串的问题。
主要参考:
马拉车算法

算法思想:

  1. 解决回文串的奇偶性的问题
    主要办法:在每个字符间插入"#",并且为了使得扩展的过程中,到边界后自动结束,在两端分别插入 “^” 和 “$”

原字符串

在这里插入图片描述
此时字符串长度为 2*n+3。

T数组:变换后的数组
P数组:掉 “#” 的原字符串的总长度

我们以下图为例:

P[ 6 ] = 5;代表的意思为:左边扩展 5 个字符,相应的右边也是扩展 5 个字符,也就是 “#c#b#c#b#c#”。而去掉 # 恢复到原来的字符串,变成 “cbcbc”,它的长度刚好也就是 5
在这里插入图片描述

  1. 求原字符串的下标
    我们需要借助P数组,比如:求P[i] = 5 的原字符串的下标。index = 6;(index - p[i])/2 = 0;
    因此,只需要返回原字符串的0~4的下标。
    综上:公式为:(index - p[i])/2 ~ P[i] -1。
  2. 求P数组
    求P数组是算法的核心。首先,我们做如下定义:
    1)C:回文串的中心
    2)R:回文串的右边半径,即:R = C + P[i]。就是当前回文串中心,最右边
    3)i_mirror :当前需要求的第 i 个字符关于 C 对应的下标在这里插入图片描述
    此时,我们求P[i],可以利用回文串的对称性,P[i] = P[i_mirror]。但是这个并不是绝对的。
    考虑如下几种特殊情况:
    1. p[i_mirror] 较大,超过了R的边界。
      在这里插入图片描述

当我们要求 P [ i ] 的时候,P [ mirror ] = 7,而此时 P [ i ] 并不等于 7 ,为什么呢,因为我们从 i 开始往后数 7 个,等于 22 ,已经超过了最右的 R ,此时不能利用对称性了,但我们一定可以扩展到 R 的,所以 P [ i ] 至少等于 R - i = 20 - 15 = 5,会不会更大呢,我们只需要比较 T [ R+1 ] 和 T [ R+1 ]关于 i 的对称点就行了,就像中心扩展法一样一个个扩展。
此时,我们同时需要更新 C 和 R 为当前的回文串了。因为我们必须保证 i 在 R 里面,所以一旦有更右边的 R 就要更新 R。

  1. p[i_mirror] 较小,触发了原字符串的左边界。

在这里插入图片描述
此时P [ i_mirror ] = 1,但是 P [ i ] 赋值成 1 是不正确的,出现这种情况的原因是 P [ i_mirror ] 在扩展的时候首先是 “#” == “#” ,之后遇到了 "^"和另一个字符比较,也就是到了边界,才终止循环的。而 P [ i ] 并没有遇到边界,所以我们可以继续通过中心扩展法一步一步向两边扩展就行了。

  1. i=R,此时P[i] = 0

代码

 public String longestPalindrome(String s) {
        return longest(s);
    }

    private String longest(String s) {
        String temp = preProcess(s);
        int n = temp.length();
        int[] P = new int[n];
        int C = 0;
        int R = 0;
        for(int i = 1;i<n-1;i++){
            if(i < R){
                int i_mirror = 2*C - i;
                P[i] = Math.min(P[i_mirror],R-i);
            }else{
                P[i] = 0;
            }
            while(temp.charAt(i+P[i]+1) == temp.charAt(i-P[i]-1)){
                P[i]++;
            }
            if(i + P[i] > R){
                C = i;
                R = i+P[i];
            }

        }
        int maxLen = 0;
        int curIndex = 0;
        for(int i = 0;i<n;i++){
            if(P[i] > maxLen){
                curIndex = i;
                maxLen = P[i];
            }
        }
        int begin = (curIndex - maxLen)/2;
        return s.substring(begin,begin + maxLen);
    }

    public String preProcess(String s){
        if(s.length() == 0){
            return "^$";
        }
        String res = "^";
        for(int i = 0;i<s.length();i++){
            res += ("#" + s.charAt(i));
        }
        res += "#$";
        return res;
    }
    

测试代码

public static void main(String[] args) {
        String test = "babad";
        Exec005 a = new Exec005();
        String s = a.longestPalindrome(test);
        System.out.println(s);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值