Manacher算法

Manacher算法的一些思考

  1. 回文字符串有两种形式:一种是奇数性:比如“aba”,一种是偶数形式:比如“abba”。
  2. 使用Manacher算法时候, 每个字符之间插入一个特殊字符串,并且两边也会插入,这个特殊字符要保证原字符串中没有的字符。这样无论原来字符串长度是奇数还是偶数,添加之后都会变成奇数。列如:
    “aba” ===》“#a#ba#”。
    “abba” ===>“#a#b#b#a#”。
    并且回文字符串第一个和最后一个字符一定是这个特殊字符。
  3. 回文半径:我们定义回文子串最中间的那个字符到回文子串最左边的字符的长度叫回文半径
    在这里插入图片描述
    列如:
    在这里插入图片描述
  4. 中心扩散以当前字符为中心扩散完成,下一个字符还要重新计算,有没有一种方法不重新计算?
    假如以当前字符s[maxCenter]为回文中,最大回文长度是从left到maxRight
    如果我们想求以字符s[i]为回文中心的最大回文长度,我们只需要找到 i 关于maxCenter的对称点 j 的最大回文长度,j 已经计算过了我们知道他的最大回文长度
    在这里插入图片描述
  • 如果 i 在maxRight的左边,并且j的最大回文长度左边没有到达left,根据对称性 i 的最大回文长度就等于 j 的最大回文长度。
    p[i] = p[j] = p[maxCenter*2-i]。
  • 如果 i 在maxRight的左边,并且j的最大回文长度左边到达left或者超过left,根据对称性,i 最小回文长度等于 j - left 也等于
    maxRight -i,最大多大还要继续判断
  • 如果 i 在maxRight的右边,需要一个一个算。

Manacher算法变量

  1. pArr :一个整型数组,长度和预处理串一样,存每个位置的最长回文半径是多少.
  2. i:整型,当前遍历到的位置,因为pArr[0]=1, 所以i可以从1开始遍历.
  3. r:整型,回文最右边界,只要某个位置能扩到超过这个位置,就更新r这个值,初始值为0,因为一个字符串回文字符串至少是1,可以以第0个字符为中心且以0为最右边界(即:第0个字符本身作为一个回文串).
  4. c:整型,就是扩到r位置的的中心点,即pArr[c] = r - c + 1,初始值为0,与r的初始值定为0一样的考虑

Manacher算法步骤

  1. 判断 i > r ,如果大于 初始半径为1,直接暴力解法。
  2. 如果小于 ,初始半径为 pArr[c-(r-c)] 或 r-i+1 找一个最小的就可以。然后暴力解法。
  3. 半径比以前的大就跟新 r 和c。
  4. 最大半径赋值。
  5. i++ 遍历(1-4步骤)
    6.完成后 定位最大回文有边界的回文中心是哪个。构造最大回文子串。

Manacher算法代码

public class LeetCode_0005_LongestPalindromicSubstring {
    
    public static String longestPalindrome(String s) {
        if (s == null || s.length() <= 1) {
            return s;
        }
        char[] str = s.toCharArray();
        char[] strs = manacherStr(str);
        int[] pArr = new int[strs.length];
        int c = 0;
        int r = 0;
        int i = 1;
        int len = strs.length;
        int max = 1;
        while (i < len) {
            // pArr[i] 至少不需要扩的大小
            pArr[i] = i < r ? Math.min(r - i, pArr[c - (i - c)]) : 1;
            // 暴力扩
            while (i + pArr[i] < len && i - pArr[i] >= 0) {
                if (strs[i + pArr[i]] == strs[i - pArr[i]]) {
                    pArr[i]++;
                } else {
                    break;
                }
            }
            // 扩散的位置能否更新回文有边界R
            // 如果可以更新,则更新R,且把C置于当前的i,因为是当前的i让回文右边界扩散的
            if (i + pArr[i] > r) {
                r = i + pArr[i];
                c = i;
            }
            max = Math.max(pArr[i++], max);
        }

        // 定位最大回文有边界的回文中心是哪个
        int n = 0;
        for (; n < len; n++) {
            if (pArr[n] == max) {
                break;
            }
        }

        // 构造最大回文子串
         // 想一想开头和结尾一定是处理的特殊字符'#',所以 n - max +2 开始 ,每次加两步
        StringBuilder sb = new StringBuilder();
        for (i = n - max + 2; i < n + max; i += 2) {
            sb.append(strs[i]);
        }
        return sb.toString();
    }

    public static char[] manacherStr(char[] str) {
        char[] strs = new char[str.length << 1 | 1];
        for (int i = 0; i < strs.length; i++) {
            strs[i] = ((i & 1) == 1) ? str[i >> 1] : '#';
        }
        return strs;
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

非常@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值