[Leetcode] 5. Longest Palindromic Substring 和 [Leetcode] 516. Longest Palindromic Subsequence 最长回文子序

目录

1、 [Leetcode] 5. Longest Palindromic Substring

(1)、问题描述

(2)、思路分析

(3)、Java代码

2、[Leetcode] 516. Longest Palindromic Subsequence

(1)、 问题描述

​​​​​​ (2)、 思路分析

 (3)、 Java代码


1、 [Leetcode] 5. Longest Palindromic Substring

(1)、问题描述

/****Leetcode_5_LongestPalindromicSubstring_Medium***/
/**
 * Leetcode_5_LongestPalindromicSubstring_Medium
 * 难度:Medium
 * 类型:动态规划Dynamic Programming
 * <p>
 * https://leetcode.com/problems/longest-palindromic-substring/
 * 题目描述:
 * Given a string s, find the longest palindromic substring in s.
 * You may assume that the maximum length of s is 1000.
 * Example 1:
 * Input: "babad"
 * Output: "bab"
 * Note: "aba" is also a valid answer.
 * Example 2:
 * Input: "cbbd"
 * Output: "bb"

(2)、思路分析

* 思路分析:
* 方法1:暴力扩展。
* 选择一个点,然后向两边扩展,分成两种情况:
* 1. 奇数回文:起点就是一个点,然后通过extendedPalindrome方法向两边扩展。
* 2. 偶数回文:起点是两个相邻的点,然后通过extendedPalindrome方法向两边扩展。
* 扩展是通过while循环实现的,一次扩展结束后需要更新最大回文长度。
* 方法2:动态规划思路;采用二维动态数组。
* dp[start][end]表示start到end的子字符串如果回文,则其值就是字符串的长度;如果不是回文,就是0;
* dp[start][end]=1;                            when start == end;
* dp[start][end] = dp[start + 1][end - 1] + 2; when chars[start] == chars[end] && dp[start + 1][end - 1] == perLen - 2
* dp[start][end] = 0;                          otherwise
* 动态规划方法改进:
* 当前长度子字符串的回文长度的获取只与前两个长度的状态有关,因此只需要三个一维数组即可。
* 三个一维数组每次循环交换索引即可。
*     dp[0]填充当前长度对应的数据;
*     dp[1]填充当前长度-1对应的数据;
*     dp[2]填充当前长度-2对应的数据;

(3)、Java代码

 /**
     * 中心扩展法
     */
    public String longestPalindrome2(String str) {
        if (str == null || str.length() == 0) return "";
        if (str.length() == 1) return str;
        char[] chars = str.toCharArray();
        int[] result = new int[2];
        for (int start = 0; start < str.length(); start++) {
            extendedPalindrome(chars, start, start, result);//for odd length
            extendedPalindrome(chars, start, start + 1, result);//for even length
        }
        return str.substring(result[0], result[0] + result[1]);
    }

    //扩展方法
    public void extendedPalindrome(char[] chars, int startLeft, int startRight, int[] result) {
        while (startLeft >= 0 && startRight < chars.length && startLeft <= startRight &&
                chars[startLeft] == chars[startRight]) {
            startLeft--;
            startRight++;
        }
        int len = startRight - startLeft - 1;
        if (len > result[1]) {
            result[0] = startLeft + 1;
            result[1] = len;
        }
    }

    /**
     * 动态规划方法
     * 二维动态数组
     */
    public String longestPalindrome(String str) {//TwoDimension
        if (str == null || str.length() == 0) return "";
        char[] chars = str.toCharArray();
        int len = chars.length;
        int[][] dp = new int[len][len];
        int start, end, maxStart = 0, maxEnd = 0, max = 0;
        for (int perLen = 1; perLen <= len; perLen++) {
            for (start = 0; start <= len - perLen; start++) {
                end = start + perLen - 1;
                if (start == end) dp[start][end] = 1;
                else if (chars[start] == chars[end] &&
                        dp[start + 1][end - 1] == perLen - 2) {//后一个条件很重要
                    dp[start][end] = dp[start + 1][end - 1] + 2;
                } else dp[start][end] = 0;
                if (dp[start][end] > max) {
                    max = dp[start][end];
                    maxStart = start;
                    maxEnd = end;
                }
            }
//        printArray(dp);
        }
        return str.substring(maxStart, maxEnd + 1);
    }

    /**
     * 动态规划方法改进--三个一维数组
     * 降低空间复杂度
     * dp[0]填充当前长度对应的数据;
     * dp[1]填充当前长度-1对应的数据;
     * dp[2]填充当前长度-2对应的数据;
     */
    public String longestPalindrome_DP2(String str) {//TwoDimension
        if (str == null || str.length() == 0) return "";
        char[] chars = str.toCharArray();
        int len = chars.length;
        int[][] dp = new int[3][len];
        int start, end, maxStart = 0, maxEnd = 0, max = 0;
        for (int perLen = 1; perLen <= len; perLen++) {
            for (start = 0; start <= len - perLen; start++) {
                end = start + perLen - 1;
                if (start == end) dp[0][start] = 1;
                else if (chars[start] == chars[end] &&
                        dp[2][start + 1] == perLen - 2) {//后一个条件很重要
                    dp[0][start] = dp[2][start + 1] + 2;
                } else dp[0][start] = 0;
                if (dp[0][start] > max) {
                    max = dp[0][start];
                    maxStart = start;
                    maxEnd = end;
                }
            }
            swap(dp);
        }
        return str.substring(maxStart, maxEnd + 1);
    }

    //交换dp[3][]中的三个一维数组
    private void swap(int[][] dp) {
        printArray(dp);
        int[] temp = dp[2];
        dp[2] = dp[1];
        dp[1] = dp[0];
        dp[0] = temp;
        printArray(dp);
    }

    /**
     * 辅助debug方法:打印二维数组
     */
    private void printArray(int[][] dp) {
        for (int i = 0; i < dp.length; i++)
            System.out.println(Arrays.toString(dp[i]));
        System.out.println("###################################");
    }

 

2、[Leetcode] 516. Longest Palindromic Subsequence

(1)、 问题描述

/*******Leetcode_516_LongestPalindromicSubsequence_Medium*****/
/***
* Leetcode_516_LongestPalindromicSubsequence_Medium
* Given a string s, find the longest palindromic subsequence's length in s.
* You may assume that the maximum length of s is 1000.
* Example 1:
* Input:"bbbab"
* Output:4
* One possible longest palindromic subsequence is "bbbb".
* Example 2:
* Input: "cbbd"
* Output:  2
* One possible longest palindromic subsequence is "bb".
*  注意:substring与subSequence的区别:
*  substring是连续的子字符串;而subSequence是不连续的但保持相对顺序的子字符串。


​​​​​​ (2)、 思路分析

思路分析:
* 方法1:利用二维的动态规划。
* dp[start][end]表示从start到end子字符串中最大回文序列的长度;
* 其中perLen = end-start+1;表示子字符串的长度;
* 嵌套两层循环:(1)外层循环:对字符串的长度的遍历,长度从1到len;
*          每循环一次,对应dp[][]二维数组的对角线数据更新一次;
*          如perLen=1,对应主对角线,perLen=2对应右上的一条对角线,perLen=len,对应右上角一个点。
* (2)内层循环:对start的起点的遍历,从0到len-perLen。
*   核心:更新值的规律:
*   dp[start][end]=1, when start == end;
*   dp[start][end] = dp[start + 1][end - 1] + 2; start != end&&chars[start] == chars[end]
*   dp[start][end] = Math.max(dp[start + 1][end], dp[start][end - 1]);otherwise
*
*   复杂度分析:
*   空间复杂度O(N*N);时间复杂度O(N*N)
*
* 方法2:降低空间复杂度。
* 由于根据子字符串长度遍历时,每次当前长度对应的数值只依赖于当前长度减一和减二对应的值,
* 因此只需要保留三个一维数组即可,这样就可以降低空间复杂度。
* 每次循环都需要轮流交换三个数组的索引即可。
* dp[0]表示当前长度的数值;
* dp[1]表示当前长度减一的数值;
* dp[2]表示当前长度减二的数值。
*/

 (3)、 Java代码

/**
 * 二维的动态规划dp[len][len]
 */
public int longestPalindromeSubseq(String seq) {//TwoDimension
    if (seq == null || seq.length() == 0) return 0;
    char[] chars = seq.toCharArray();
    int len = chars.length;
    int[][] dp = new int[len][len];
    int start, end;
    for (int perLen = 1; perLen <= len; perLen++) {
        for (start = 0; start <= len - perLen; start++) {
            end = start + perLen - 1;
            if (start == end) dp[start][end] = 1;
            else if (chars[start] == chars[end]) dp[start][end] = dp[start + 1][end - 1] + 2;
            else dp[start][end] = Math.max(dp[start + 1][end], dp[start][end - 1]);
        }
    }
    return dp[0][len - 1];
}

/**
 * 方法2:降低空间复杂度dp[3][len]
 * 将二维数组变成三个一维数组
 * 降低空间复杂度
 * dp[0]填充当前长度对应的数据;
 * dp[1]填充当前长度-1对应的数据;
 * dp[2]填充当前长度-2对应的数据;
 */
public int longestPalindromeSubseq(String seq) {//OneDimension
    if (seq == null || seq.length() == 0) return 0;
    char[] chars = seq.toCharArray();
    int len = chars.length;
    int[][] dp = new int[3][len];//三个一维数组
    int start, end;
    for (int perLen = 1; perLen <= len; perLen++) {
        for (start = 0; start <= len - perLen; start++) {
            end = start + perLen - 1;
            if (start == end) dp[0][start] = 1;
            else if (chars[start] == chars[end]) dp[0][start] = dp[2][start + 1] + 2;
            else dp[0][start] = Math.max(dp[1][start], dp[1][start + 1]);
        }
        swap(dp);
    }
    return dp[1][0];
}

//交换dp[3][]中的三个一维数组
private void swap(int[][] dp) {
    int[] temp = dp[2];
    dp[2] = dp[1];
    dp[1] = dp[0];
    dp[0] = temp;
}

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值