Manacher算法求解字符串的最长回文子串

package jinjie_class_01;

import java.util.Arrays;

/**
 * Manacher算法
 * @author lenovo
 *
 */
public class Code_04_Manacher {

    public static char[] manacherString(String str) {

        char[] charArr = str.toCharArray();
        char[] res = new char[str.length() * 2 + 1];
        int index = 0;
        for (int i = 0; i != res.length; i++) {
            // i & 1  可以用作判断奇数跟偶数  两个操作数中位都为1,结果才为1,否则结果为0
            res[i] = (i & 1) == 0 ? '#' : charArr[index++];  
        }
        return res;
    }




    public static int maxLcpsLength(String str) {
        if (str == null || str.length() == 0) {
            return 0;
        }
        char[] charArr = manacherString(str);
        int[] pArr = new int[charArr.length];  // 回文半径数组
        int index = -1;   // 最右回文边界的中心点
        int pR = -1;      // 最右回文边界
        int max = Integer.MIN_VALUE;
        for (int i = 0; i != charArr.length; i++) {  // 求每一个i位置的回文中心

            // >> 1. i 如果在  R的外面,那么就是暴力继续往外扩
            //       当 i 在 R的外面,或者  i 就在 R 上,就认为我不用扩的区域就只有一个   就是自己


            // >> 2. i 在R里面,  为了加速,认为地分为三种情况  
            //  三种情况: a.i`的回文左右半径在L与R之间     b. i`的回文左半径超过了 L  c. i`的回文左半径在L上(压线)
            // i`的回文和i到R的距离   哪个小, 哪个就起码是i位置的回文半径   (这个半径还可能继续往外扩,现在得到的长度是起码的长度)
            // index是取得最右边界的回文中心点
            // 当  i 在回文右边界的里面,就起码有一个不用去验证的区域,不用验证,就知道这个块区域是回文的
            /**
             *  此时得到的pArr[i]  只是起码不用验证的区域 (一部分回文区域)
             */
            pArr[i] = pR > i ? Math.min(pArr[2 * index - i], pR - i) : 1;  




            /**
             * 可以按照分析地那样,分出四种情况  (两种大范围的情况)
             * 情况1.   i 如果在  R的外面
             * 情况2.   i 在R里面  i`的回文左右半径在L与R之间
             * 情况3.   i 在R里面  i`的回文左半径超过了 L
             * 情况4.   i 在R里面  i`的回文左半径在L上(压线)
             */



            // 老师的代码,有一个起码不用验的区域之后,还让你扩一下,如果是情况2跟情况3  O(1)  直接出结论这两种情况的话,那么继续往外扩一下,就会失败
            while (i + pArr[i] < charArr.length && i - pArr[i] > -1) {  // 先判断 需要验证的区域 左边 跟 右边 是否越界
                if (charArr[i + pArr[i]] == charArr[i - pArr[i]])   // 扩出来的左右两边位置相等   回文半径 就 +1
                    pArr[i]++;
                else {

                    break;

                }
            }


            if (i + pArr[i] > pR) { // 扩出来的区域超过了之前的最右回文边界
                pR = i + pArr[i];  // 更新最右回文边界
                index = i;         // 更新最右回文边界的中心点
            }
            max = Math.max(max, pArr[i]);  // 记录全局最大值
        }
        System.out.println(Arrays.toString(pArr));
        return max - 1;
    }



    public static void main(String[] args) {

        String str1 = "abc1234321ab";
        System.out.println(maxLcpsLength(str1));
    }


}

猜你喜欢

转载自blog.csdn.net/qq_38200548/article/details/82048784
今日推荐