Manacher算法

计算字符串的最大回文字符数,难点:奇回文与偶回文
解决方法:在字符串中插入特殊字符
如:11311--->#1#1#3#1#1#,无论是奇数还是偶数个字符,都可以变成奇数。
马拉车算法:O(N)

  1. 准备辅助数组arr[],记录每个位置的回文半径,用前面的记录为后面加速。
  2. 记录最右回文右边界R
  3. 记录最右回文右边界R第一次对应的中心C

流程:

  1. 当前i不在R里面,直接暴力扩展
  2. i在R内,i'是i关于C的对称点:
    • i'的回文直径在C的回文直径【L,R】内,此时不用扩展,arr[i]=arr[i']
    • i'的回文左边界
    • i'的回文左边界=L,压线,此时i的回文半径需要去试

相关题:
在一个字符串后面添加最少字符是的其成为一个回文串。
解法:找到第一个字符,其回文右边界R包含字符串最后一个字符,则在字符串后面加上L前的字符串逆序即可。
如:abc12321,遍历到‘3’时,R包含1,L为左边1的位置,此时在后面加上cba即可。

Manacher代码实现:

private static char[] manacherString(String str){
        StringBuilder sb = new StringBuilder("#");
        for(int i = 0; i < str.length(); i++){
            sb.append(str.charAt(i));
            sb.append('#');
        }
        return sb.toString().toCharArray();
    }

    public static int manacher(String str) {
        if(str == null || str.length() == 0){
            return 0;
        }
        char[] charArr = manacherString(str);
        int[] pArr = new int[charArr.length]; // 回文半径数组
        int C = -1; // R对应的中心位置
        int R = -1; // 最右回文右边界
        int max = Integer.MIN_VALUE;
        for(int i = 0; i < charArr.length; i++){
            pArr[i] = R > i ? Math.min(pArr[2*C-i], R-i) : 1; // i在R里面,比较对称点回文半径
            while(i + pArr[i] < charArr.length && i - pArr[i] > -1){
                if(charArr[i + pArr[i]] == charArr[i - pArr[i]]){
                    pArr[i]++;
                } else {
                    break;
                }
            }
            if(i + pArr[i] > R){ // 更新C,R
                R = i + pArr[i];
                C = i;
            }
            max = Math.max(max, pArr[i]);
        }
        return max - 1; // 最终回文串长度是回文半径-1
    }

你可能感兴趣的:(Manacher算法)