左神系列|万万不能忘记的Manacher算法笔记

Manacher算法

简介

Manacher算法主要应用于判断最长回文子串的问题

Manacher算法的步骤

在说步骤之前,先说下暴力的解决的方案,遍历字符串的每个字符,以每个字符为中心,往外扩,记录往外扩的最大长度,即为最长回文子串的长度。代码最后展示

言归正传,我们来说说Manacher算法是怎么解决的!

它分为四种情况,在说四种情况前,我们先来明确算法中的几个基础变量:C,R,i,i’,pArr[i],先解释下这些都是干嘛的。

  • C:表示和R对应的中心字符
  • R:表示回文子串的右边界:可能这块有点别扭,意思就是,这个R是只能增,它记录的是,往右遍历的过程中,最后一个回文子串的右边界
  • i:遍历的过程中对应的字符索引
  • i’:和i关于C对称的字符索引
  • pArr[i]:表示以i为中心的回文半径(长度算上i本身)

解释完这些变量的含义,我们来分析下它的步骤:

  1. 当i在R外的时候,只能暴力扩,没有优化,扩张成功,R增加
  2. 当i在R内的时候,我们找到i关于C对称的i’,
    1. 当i+i’的回文半径之后涉及范围超过R的时候,不用扩,直接出答案
    2. 当i+i’的回文半径之后涉及范围在R上的时候,不用扩,直接出答案
    3. 当i+i’的回文半径之后涉及范围在R内的时候,往外扩,扩成功,R增加

我们来看下实现代码:(唉,不知道这个啥时候又会忘,我很绝望呀!)

/**
 * Author:markusZhang
 * Date:Create in 2020/7/27 21:55
 * todo: Manacher算法:解决最长回文字符串问题
 */
public class Manacher_Str {
    /**
     * 暴力求解
     */
    public static int process_1(String s){
        if (s == null || s.length() == 0){
            return 0;
        }
        char []str = s.toCharArray();
        str = manageStr(str);
        int max = Integer.MIN_VALUE;
        for (int i=0;i<str.length;i++){
            int len = 1;
            int left = i-1;
            int right = i+1;
            while(left > -1 && right < str.length){
                if (str[left] == str[right]){
                    len++;
                }else{
                    break;
                }
                left--;
                right++;
            }
            max = Math.max(max,len);
        }
        return max-1;
    }
    /**
     * 针对暴力进行加速
     */
    public static int process_2(String s){
        if (s == null || s.length() == 0){
            return 0;
        }
        char []str = s.toCharArray();
        str = manageStr(str);
        //以下四个变量是整个算法最基础的部分
        int max = Integer.MIN_VALUE;//最后返回的结果
        int []pArr = new int[str.length];//用于记录以每个元素为中心往外扩的最大半径
        int C = -1;// 中心位置
        int R = -1;// 扩充成功的下一个位置
        for (int i = 0 ; i < str.length ; i++){
            //这句话的意思是,i在R内吗?不在的话就直接为1,如果在R内的话,结果为i到R的距离与i'的扩充半径的最小值
            pArr[i] = i<R?Math.min(pArr[2*C-i],R-i):1;
            while(i-pArr[i] > -1 && i+pArr[i] < str.length){
                if (str[i-pArr[i]] == str[i+pArr[i]]){
                    pArr[i]++;
                }else{
                    break;
                }
            }
            if (i+pArr[i] > R){
                R = i+pArr[i];
                C = i;
            }
            max = Math.max(max,pArr[i]);
        }
        return max-1;
    }
    /**
     * 处理字符串使字符串的前后和每个字符之间填充'#'
     */
    private static char[] manageStr(char[] str){
        StringBuilder sb = new StringBuilder();
        sb.append("#");
        for (int i=0;i<str.length;i++){
            sb.append(str[i]);
            sb.append("#");
        }
        return sb.toString().toCharArray();
    }
    public static void main(String[] args) {
        String s = "babad";
        System.out.println(process_1(s));

        System.out.println(process_2(s));
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值