LeetCode-5. 最长回文子串-Java-medium

题目链接

法一(动态规划)
    /**
     * 法一(动态规划)
     * 时间复杂度:O(n^2)
     * 1. 确定dp数组以及下标的含义
     *    dp[i][j]表示从s[i]到s[j]之间的子串是否是回文子串,若是,dp[i][j] = true,否则,dp[i][j] = false
     * 2. 确定递推公式
     *   (1)s[i] == s[j]
     *       若s[i+1]到s[j-1]是回文子串,则s[i]到s[j]是回文子串;
     *       若s[i+1]到s[j-1]不是回文子串,则s[i]到s[j]也不是回文子串
     *   (2)s[i] != s[j]
     *       s[i]到s[j]不是回文子串
     *   (3)由此可得递推公式
     *       s[i] == s[j]   dp[i][j] = dp[i+1][j-1]
     *       s[i] != s[j]  dp[i][j] = false
     * 3. 确定dp数组如何初始化
     *   (1)dp[i][i] = true
     *   (2)s[i] == s[i+1]  dp[i][i+1] = true
     *   (3)s[i] != s[i+1]  dp[i][i+1] = false
     * 4. 确定遍历顺序
     *   (1)按子串的长度和子串的初始位置进行枚举
     *   (2)第一遍将长度为3的子串dp值求出
     *   (3)第二遍根据第一遍的计算结果计算出长度为4的子串dp值,以此类推...
     *
     * @param s
     * @return
     */
    public String longestPalindrome(String s) {
        int lens = s.length(), maxLen = 1;
        boolean[][] dp = new boolean[lens][lens]; // 数组元素是boolean型默认初始化值为false
        int left = 0, right = 0;
        for (int i = 0; i < lens; i++) {
            dp[i][i] = true;
            if (i < lens - 1 && s.charAt(i) == s.charAt(i + 1)) {
                dp[i][i + 1] = true;
                maxLen = 2;
                left = i;
                right = i + 1;
            }
        }
        for (int L = 3; L <= lens; L++) {
            for (int i = 0; i + L - 1 < lens; i++) {
                int j = i + L - 1;
                if (s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1]) {
                    dp[i][j] = true;
                    maxLen = L;
                    left = i;
                    right = j;
                }
            }
        }
        return s.substring(left, right + 1);
    }
法二(动态规划)
    /**
     * 法二(动态规划)
     * 时间复杂度:O(n^2)
     * 1. 确定dp数组以及下标的含义
     *    dp[i][j]表示区间范围[i,j]的子串是否是回文子串,若是,dp[i][j] = true,否则,dp[i][j] = false
     * 2. 确定递推公式
     *   (1)s[i] == s[j]
     *       若下标i与j相同,是回文子串
     *       若下标i与j相差为1,是回文子串
     *       若下标i与j相差大于1,则看dp[i + 1][j - 1]是否为true,若为true,则是回文子串,否则不是回文子串
     *   (2)s[i] != s[j]
     *       s[i]到s[j]不是回文子串
     *   (3)由此可得递推公式
     *       s[i] == s[j]  j - i <= 1 ? dp[i][j] = true : dp[i][j] = dp[i+1][j-1]
     *       s[i] != s[j]  dp[i][j] = false
     * 3. 确定dp数组如何初始化
     *    dp[i][j] = false
     * 4.确定遍历顺序
     *    dp[i + 1][j - 1]在dp[i][j]的左下角
     *    所以一定要从下到上,从左到右遍历,这样保证dp[i + 1][j - 1]都是经过计算的
     *
     * @param s
     * @return
     */
    public String longestPalindrome_2(String s) {
        int lens = s.length(), maxLen = 1;
        boolean[][] dp = new boolean[lens][lens]; // 数组元素是boolean型默认初始化值为false
        int left = 0, right = 0;
        for (int i = lens - 1; i >= 0; i--) {
            for (int j = i; j < lens; j++) {
                if (s.charAt(i) == s.charAt(j) && (j - i <= 1 || dp[i + 1][j - 1])) {
                    dp[i][j] = true;
                }
                if (dp[i][j] && j - i + 1 > maxLen) {
                    maxLen = j - i + 1;
                    left = i;
                    right = j;
                }
            }
        }
        return s.substring(left, right + 1);
    }
法三(双指针)
    /**
     * 法三(双指针)
     * 时间复杂度:接近O(n)
     *
     * @param s
     * @return
     */
    public String longestPalindrome_3(String s) {
        if (s == null || s.length() == 0) {
            return "";
        }
        int[] range = new int[2]; // 保存起始位置
        char[] str = s.toCharArray();
        for (int i = 0; i < s.length(); i++) { // 把回文看成中间的部分全是同一字符,左右部分相对称
            i = findLongest(str, i, range); // 找到下一个与当前字符不同的字符
        }
        return s.substring(range[0], range[1] + 1);
    }

    /**
     * 根据当前字符向两边扩散,返回下一个与当前字符不同的字符
     *
     * @param str
     * @param left
     * @param range
     * @return
     */
    private static int findLongest(char[] str, int left, int[] range) {
        int right = left; // left为中间部分的第一个字符
        while (right < str.length - 1 && str[right + 1] == str[left]) { // 查找中间部分
            right++;
        }
        int ans = right; // right为中间部分的最后一个字符
        while (left > 0 && right < str.length - 1 && str[left - 1] == str[right + 1]) { // 从中间向左右扩散
            left--;
            right++;
        }
        if (right - left > range[1] - range[0]) { // 记录最大长度
            range[0] = left;
            range[1] = right;
        }
        return ans;
    }
本地测试
        /**
         * 5. 最长回文子串
         */
        lay.showTitle(5);
        Solution5 sol5 = new Solution5();
        String s5 = "babad";
        System.out.println(sol5.longestPalindrome(s5));
        System.out.println(sol5.longestPalindrome_2(s5));
        System.out.println(sol5.longestPalindrome_3(s5));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值