Manacher算法总结

算法应用 :解决字符串中的回文串问题


其中涉及到的结构

(1)回文直径:以一个中心,向左右两边扩张,扩出来的区域总长
(2)回文半径:为回文直径一半的长度                   
(3)最右回文右边界:R(只要有回文串的长度 的右边界 > R 那么R就会扩展)

算法思路

(1)先将给定的字符串进行处理,各个字符之间和字符串的首尾添加上“#”字符 (避免给定的字符串需要区分长度为奇数或偶数的可能性)

(2)对处理好的字符串进行遍历 求出每个位置的最长回文半径 通过遍历这一遍即可得出 最长回文子串的长度

(3)到每个位置求最长回文半径 以当前位置为中心点的回文半径大于R(最右回文右边界) 那么R就向外扩,且中心点 C 改为当前位置i 只要有一次不同那么就终止过程

向外扩张的过程中分为四种可能:

<1>当前位置 i 在 R 的外侧 那么 R 直接向外扩

<2>当前位置 i 在 R 的内侧 若 i'(为 i 在当前的整个回文边界中通过中心点映射的位置)的整个回文直径 在当前中心点 C 整个回文边界内(不在边界上 < L)那么 i 的回文半径就是 i'的 回文半经

<3>当前位置 i 在 R 的内侧 但是 i’ 的左边界超出了中心点 C 的左边界(i'的L < C的L)那么只需要判断当前R的下一个是否跟映射到i‘位置的字符相同即可 直接向外进行扩张

<4>当前位置 i 在 R 的内侧 但是 i’的左边界 跟 C的左边界 存在重合的问题 那么就需要对R的下一个位置 和以i为中心 以i‘的回文半径的长度 向i的左边进行比对 若比对成功则向右扩


举例

求出最大回文子串的长度

 //    对给定的字符串进行处理 给字符串的首尾 和 各个字符中间添加上“#”
    public static char[] manacherString(String s) {
        char[] chars = s.toCharArray();
        int index = 0;
        char[] str = new char[s.length() * 2 + 1];
        for (int i = 0; i < str.length; i++) {
//            判断i是为奇数还是为偶数 若为奇数则添加“#” 若为偶数则添加字符
            str[i] = (i & 1) == 0 ? '#' : chars[index++];
        }
        return str;
    }

    //    对处理好的字符串进行manacher
//    求出最大回文子串
    public static int maxLcpsLength(String s) {
        if (s.length() == 0 || s == null) {
            return 0;
        }
        char[] res = manacherString(s);
//        存储回文半径的数组
        int[] half = new int[res.length];
//        回文中心点C
        int C = -1;
        int R = -1;//最右回文右边界再向右的一个位置  整个的回文有效区域为 0..R-1 位置
        int max = Integer.MIN_VALUE;//求出的最大回文子串的长度

        for (int i = 0; i < half.length; i++) {

//            先对当前 i 位置 求出最小的回文子串的长度
//            找出不用验证的长度(节省时间复杂度)
//            四种可能均可用当前的
//            (1)i > R 直接暴力扩张
//            (2)i < R 而且i‘整个边界均在C的整个边界中 那么i = i'的回文边界长度(half[2*C-i]代表着 i'的回文半径长度)
//            (3)i < R 而且i‘的左边界跟C的左边界重合  那么i = i'的回文边界长度
//            (4)i < R i’的左边界在C的左边界外 那么 i = R-i(当前位置到最右回文边界)
            half[i] = R > i ? Math.min(half[2 * C - i], R - i) : 1;

//            让每一种可能性的 i 都去进行比对
//            没有超过边界
            while (half[i] + i < s.length() && i - half[i] > -1) {
//                在i的左右两边进行判断是否为相同的 如果相同那么回文半径++
                if(half[i+half[i]] == half[i-half[i]]){
                    half[i]++;
                }
                else {
//                    只要有一次不匹配那么就终止匹配
                    break;
                }
            }

//            对R和C进行更新操作
//            判断当前的i+half[i]是否大于当前的R
            if(i + half[i] > R){
//                如果大于R那么就执行更新操作
                R = i+half[i];
                C = i;
            }
//            对每个位置求完后都进行判断 看看是不是比之前求出的最大回文半径更大了
            max  = Math.max(max,half[i]);
        }
//        最大回文半径 与 最大回文子串之间的关系 就是
//        最大回文半径 - 1 = 最大回文子串长度
        return max-1;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值