字符串之——Manacher算法,解决最长回文问题!

回文:就是正着读和反着读一样

  1. 举例:
    1. ABCECBADE的回文子串就是就是:ABCECBA
    2. ABBABCF的子串就是:ABBA

回文问题的经典解法

  1. 加#:为了解决奇数回文字符串
    1. 从每个位置的从左从右对比:如果一遍直接越界限:即为1,如果有值的情况下越界,则不进行累加
    2. 如果一样,则累加上左右两边即:1+2 
    3. 还是拿上面的例子来举:#A#B#C#E#C#B#A#D#E#
      1. #A#B#C#E#C#B#A#
        1. 一样的长度为15 :15/2(除去#的长度)=  7
  2. 问题来了:加的这个字符有没有要求说是原来的字符串中没出现过的
    1. 解:不要求,不会影响的最终答案,永远是你加的东西和加的东西比,字符串中原本存在的和存在的比!
  3. 经典解法的时间复杂度为O(n^2)

Manacher的时间复杂度为O(n)

  1. 和kmp算法有点像,是因为存在一个加速过程。类似于KMP的arr

需要知道的几个关键信息

  1. 回文直径和回文半径
    1. 也就是回文字符串的总长度
    2. 回文半径即回文字符串一半的长度
  2. 当前回文所扩展的最右边界R
  3. 当前回文所扩展的最右边界的对称轴C
  4. 当前回文所扩展的最右边界数组。
  5. C和R的关系:C刷新,R也随之刷新。

声明几个变量

  1. index:当前坐标
  2. index‘ :当前坐标通过C的对称点

根据index的坐标位置和index‘的回文半径确定了2种大情况

  1. 第一种大情况
    1. index的坐标大于R,暴力扩就是了,index向右推移,R的数值和C的数值随之更新
  2. 第二种情况
    1. index的坐标在R内,index根据C的对称点index‘ 的回文直径     与      R根据C的对称点L之间的关系又分为3种状况
      1. index‘的回文区域在L和R范围内
        1. 这种情况index的回文就等于index‘的回文,长度一样,其次,内容一样
        2. 证明
      2. index‘的回文区域与L点重合
        1. 这种情况下,index的回文半径最小为index到R的距离,但是,是否还有更大的回文半径,需要暴力判定
        2. 证明
      3. index‘的回文区域超过L点
        1. 可以判定不用验证的区域为index到R的范围,
          1. 证明
package LiKou.Manacher;

public class Manacher {
    public static void main(String[] args) {
        String str = "abccbacfdakae";
        String over = toPrefectStr(str);
        int i = findPalindrome(over);
        System.out.println(i);
    }

    private static String toPrefectStr(String str) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("#");
        for (int i = 0; i < str.length(); i++) {
            stringBuffer.append(str.charAt(i));
            stringBuffer.append("#");
        }
        return stringBuffer.toString();
    }

    private static int findPalindrome(String str) {
        int arr[] = new int[str.length()];
        int c = -1;
        int R = -1;
        int max = -1;
        for (int i = 0; i < str.length(); i++) {

            int left = i - 1;
            int right = i + 1;
            //暴力递归
            if (i >= R) {
                //刷新i值
                R = i;
                c = i;
                int count = 0;
                arr[i] = 1;
                //往两边扩
                while (left >= 0 && right <= str.length() - 1) {
                    //如果能扩动,扩
                    if (str.charAt(left) == str.charAt(right)) {
                        //左右重新设定值
                        left--;
                        right++;
                        //R刷新
                        R++;
                        count++;
                        arr[i] += 2;
                    } else {
                        break;
                    }
                }
                max = max < arr[i] ? arr[i] : max;
            } else {
                //这下就要分情况了(在范围内,超过范围,刚刚好)
                //主要是找到i'
                int i1 = c * 2 - i;
                //然后找到i1的回文半径
                //int radius = arr[i1];
                //找到R根据C的对称点L
                int L = c - (R - c);
                //然后就要判断i1的回文半径和L的状态(在L的范围内,超出L,和L重合)
                if ((i1 - arr[i1] / 2) > L) {
                    arr[i] = arr[i1];
                } else if ((i1 - arr[i1] / 2) == L) {
                    //需要重新设定,并且看看R要不要刷新
                    //获取L'的对称点
                    int LL = i * 2 - R;
                    int sr = R + 1;
                    int sl = LL - 1;
                    arr[i] = (R - i) * 2 + 1;
                    while (sr <= arr.length - 1 && sl >= 0) {
                        if (str.charAt(sr) != str.charAt(sl)) {
                            break;
                        }
                        sl--;
                        sr++;
                        R++;
                        arr[i] += 2;
                    }
                    max = max < arr[i] ? arr[i] : max;

                } else {
                    arr[i] = (R - i) * 2 + 1;
                }

            }
        }
        return max/2;
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值