leetcode 5. 最长回文子串 [java][Manacher 算法代码]

leetcode 5. 最长回文子串

递推公式

边界条件: dp[0] = 0
已知dp[i], 可以快速向后推倒: dp[i] -> dp(i, i+dp[i])

代码

    static
            //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {

        public String longestPalindrome(String s) {
            if (s.length() < 2) {
                return s;
            }
//            动态规划, 创建Manacher算法的dp数组
            ManacherDp manacherDp = ManacherDp.create(s);
//            有了dp数组后, 计算最大回文子串
            return manacherDp.getLongestPalindrome();
        }

        /**
         * 往字符串任意两个相邻字符中间插入一个字符, 形成基数长度新的字符串
         * 封装对这个字符串的操作
         *
         * @author zhengyongpan
         * @since 2021/12/10 15:02
         */
        public static class NormalizedString {
            private String raw;
            private char seperator;
            private int length;

            /**
             * 已知NormalizedString中字符的位置, 计算原始字符串中字符的位置
             *
             * @param pos pos
             * @return int
             * @author zhengyongpan
             * @since 2021/12/10 15:27
             */
            public static int unnormalizePos(int pos) {
                return pos >> 1;
            }

            /**
             * 判断指定位置是不是特殊字符
             *
             * @param pos pos
             * @return boolean
             * @author zhengyongpan
             * @since 2021/12/10 15:27
             */
            public static boolean isSpecial(int pos) {
                return (pos & 1) == 1;
            }

            /**
             * 根据NormalizedString中的回文子串, 求原始字符串中最长回文子串的长度
             *
             * @param normalizePos    normalizePos
             * @param normalizeOffset normalizeOffset
             * @return int
             * @author zhengyongpan
             * @since 2021/12/10 15:09
             */
            public static int calcUnnormalizeLength(int normalizePos, int normalizeOffset) {
                if ((normalizePos & 1) == 0) {
//                在字符上
                    return 1 + ((normalizeOffset >> 1) << 1);
                } else {
                    //                在间隙上
                    return ((normalizeOffset + 1) >> 1) << 1;
                }
            }

            /**
             * @param centerPos centerPos NormalizedString字符串中, 回文字符串中心字符索引
             * @param offset    offset	 NormalizedString字符串中, 回文字符串臂长
             * @return int[] 原始字符串中, 回文字符串的起止位置
             * @author zhengyongpan
             * @since 2021/12/10 15:17
             */
            public static int[] toLongestPalindromePos(int centerPos, int offset) {
//                将NormalizedString中的最大回文子串, 转换为原始字符串中的最大回文子串
                int leftPos;
                int rightPos;
                if (NormalizedString.isSpecial(centerPos)) {
//                在间隙上
                    leftPos = NormalizedString.unnormalizePos(centerPos - offset + 1);
                    rightPos = NormalizedString.unnormalizePos(centerPos + offset);
                } else {
//                在字符上
                    leftPos = NormalizedString.unnormalizePos(centerPos) - (offset >> 1);
                    rightPos = NormalizedString.unnormalizePos(centerPos) + (offset >> 1);
                }
                return new int[]{leftPos, rightPos + 1};
            }

            public NormalizedString(String str, char seperator) {
                this.raw = str;
                this.seperator = seperator;
                this.length = (str.length() << 1) - 1;
            }

            public int length() {
                return (raw.length() << 1) - 1;
            }

            public char charAt(int index) {
                if ((index < 0) || (index >= length)) {
                    throw new StringIndexOutOfBoundsException(index);
                }
                if ((index & 1) == 1) {
                    return seperator;
                }
                return raw.charAt(index >> 1);
            }

            /**
             * 已知str[centerPos-offset, centerPos+offset]是回文串
             * 求以centerPos为中心的最长回文子串的臂长
             *
             * @param str       str
             * @param centerPos centerPos
             * @param offset    offset
             * @return int
             * @author zhengyongpan
             * @since 2021/12/10 11:15
             */
            private int expand(int centerPos, int offset) {
                offset++;
                int left = centerPos - offset;
                int right = centerPos + offset;
                while (left >= 0 && right < length && charAt(left) == charAt(right)) {
                    left--;
                    right++;
                }
                return ((right - left) >> 1) - 1;
            }

            /**
             * 创建dp数组
             * dp[i] 表示 NormalizedString以位置i为中心的最大回文子串的臂长
             *
             * @param
             * @return int[]
             * @author zhengyongpan
             * @since 2021/12/10 15:32
             */
            public int[] createDp() {
                int len = length();
                int[] dp = new int[len];
                int i = 1;
                while (i < len) {
                    int maxOffset = expand(i, 0);
                    dp[i] = maxOffset;
                    if (maxOffset < 2) {
                        i++;
                    } else {
                        for (int offset = 1; offset < maxOffset; offset++) {
                            int centerPos = i + offset;
                            dp[centerPos] = expand(centerPos, Math.min(dp[i - offset], maxOffset - offset));
                        }
                        i += maxOffset;
                    }
                }
                return dp;
            }

            public String getRaw() {
                return raw;
            }
        }

        public static class ManacherDp {
            private NormalizedString str;
            private int[] dp;

            private ManacherDp() {

            }

            /**
             * 创建 ManacherDp
             *
             * @param strRaw strRaw
             * @return leetcode.editor.cn.Q5_LongestPalindromicSubstring.Solution.ManacherDp
             * @author zhengyongpan
             * @since 2021/12/10 15:21
             */
            public static ManacherDp create(String strRaw) {
                ManacherDp manacherDp = new ManacherDp();
//                归一化字符串, 用特殊字符填充空隙, 得到新的长度为基数的字符串
                NormalizedString str = new NormalizedString(strRaw, '#');
                manacherDp.str = str;
//                用manacher算法进行动态规划, 计算dp数组
                manacherDp.dp = str.createDp();
                return manacherDp;
            }

            /**
             * 返回原始字符串中的最大回文子串
             *
             * @param
             * @return java.lang.String
             * @author zhengyongpan
             * @since 2021/12/10 15:21
             */
            public String getLongestPalindrome() {
//                计算原始字符串中最大回文子串的位置
                int[] longestPalindromePos = getLongestPalindromePos();
//                截取最大回文子串
                return str.getRaw().substring(longestPalindromePos[0], longestPalindromePos[1]);
            }

            /**
             * 计算原始字符串中, 最长的回文子串的位置
             *
             * @param dp dp
             * @return int[]
             * @author zhengyongpan
             * @since 2021/12/10 15:08
             */
            private int[] getLongestPalindromePos() {
//                计算NormalizedString中最大回文字串的位置
                int[] longestPalindromeInNormalizedString = getLongestPalindromeInNormalizedString();
//                将NormalizedString中回文子串的位置, 转换为原始字符串中回文子串的位置
                return NormalizedString.toLongestPalindromePos(longestPalindromeInNormalizedString[0], longestPalindromeInNormalizedString[1]);
            }

            /**
             * 计算NormalizedString中的最大回文子串
             * 以中心索引和臂长表示
             *
             * @param
             * @return int[] {中心索引, 臂长}
             * @author zhengyongpan
             * @since 2021/12/10 15:20
             */
            private int[] getLongestPalindromeInNormalizedString() {
                int len = dp.length;
//                在NormalizedString中寻找最大回文子串
                int maxOffset = 0;
                int maxPos = 0;
                int maxLength = 1;
                for (int pos = 1; pos < len; pos++) {
//                    计算原始字符串中回文子串的长度
                    int unnormalizeLength = NormalizedString.calcUnnormalizeLength(pos, dp[pos]);
                    if (unnormalizeLength > maxLength) {
                        maxOffset = dp[pos];
                        maxPos = pos;
                        maxLength = unnormalizeLength;
                    }
                }
                return new int[]{maxPos, maxOffset};
            }
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值