Manacher算法

马拉车算法主要解决的是找到一个字符串中最长的回文子串。

暴力解

暴力解的思路是遍历字符串str中的每一个字符str[i],从str[i]开始往两边扩,记录下每个以str[i]为中心的回文长度。这种方式的时间复杂度为O(n*n)。
即使是以这种方法,奇回文还能求解,偶回文便不能直接扩,否则会出错。例如“abba”,以str[0]为中心的回文长度为1,以str[1]为中心的回文长度为1,以str[2]为中心的回文长度为1,以str[3]为中心的回文长度为1,最终最长的回文子串为1,很显然出错了。
需要一个技巧,把字符串中所有相邻字母间和头尾都加一个“#”,例如:“abba”转为“#a#b#b#a#”,而“abbba”转为“#a#b#b#b#a#”,这样都转为奇数,扩的时候还是一样的扩法,奇回文和偶回文最后的答案统一都是最终得到的最长回文长度/2。

Manacher算法

先将一些需要准备的工具或概念:

  1. help[]数组,里面记录每个位置的回文半径,用以之后加速计算;
  2. 回文半径右边界:在所有的回文中最靠右边的边界位置R;
  3. 回文右边界的中心:在所有的回文中最靠右边的边界位置R对应的回文中心C。

下面就讲一下计算help[]数组的流程:在计算任意位置i时,可能出现的情况:
(先提一下变量概念:R为到目前位置的回文右边界,C为相应的中心,i’为i以C为中心的对称位置)

  1. 若位置i在回文右边界R外,暴力计算以位置i为中心的回文半径;
  2. 若位置i在回文右边界R内,
  • i’位置的回文左边界(由于help[i’]已知,故i’的回文左边界已知)在C位置的回文左边界内:i的回文半径与i’相同,不用扩;O(1)
  • i’位置的回文左边界在C位置的回文左边界外:i的回文半径为i到R的距离;O(1)
  • i’位置的回文左边界与C位置的回文左边界相同:从i到R是不用检验的,在这范围内必是回文,但是能不能在往外扩我们未知,需要从R开始往外扩去暴力尝试。

以上就讲完了所有可能性,其实画张图会比较清晰,由于时间原因,就不画了。。有了计算流程就可以写代码啦。
贴代码:

	public static int manacher(String str) {
        if (str.length() == 0) return 0;
        String s = transfer(str);
        int C = -1;//记录回文右边界的中心
        int R = -1;//记录回文右边界,开区间
        int max = Integer.MIN_VALUE;
        int[] arr = new int[s.length()];//回文半径数组
        for (int i = 0; i < arr.length; i++) {
            arr[i] = R > i ? Math.min(arr[2 * C - i], R - i) : 1;
            while (i + arr[i] < arr.length && i - arr[i] > -1) {
                if (s.charAt(i + arr[i]) == s.charAt(i - arr[i])) {
                    arr[i]++;
                } else {
                    break;
                }
            }
            if (i + arr[i] > R) {
                R = i + arr[i];
                C = i;
            }
            max = Math.max(max, arr[i]);
        }
        return max - 1;
    }

    public static String transfer(String str) {
        if (str.length() == 0) return str;
        StringBuilder sb = new StringBuilder("#");
        for (int i = 0; i < str.length(); i++) {
            sb.append(str.charAt(i));
            sb.append("#");
        }
        return String.valueOf(sb);
    }

由于回文右边界R只能向右扩不会往回走,所以时间复杂度为O(n)

相关题目

求在一个字符串末尾添加最少的字符数使的整个字符串为回文串。
解法:求包含字符串原串中最后一个字符的回文直径,前面不包含在回文直径中的字符将其逆序在字符串末尾就是要添加的最少字符。
例如:字符串“abc12321”,用马拉车算法求包含末尾字符“1”的回文直径,即“12321”,前面不包含的的字符串“abc”将其逆序添加到原串末尾为“abc12321cba”即为答案。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值