目录
1、 [Leetcode] 5. Longest Palindromic Substring
2、[Leetcode] 516. Longest Palindromic Subsequence
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;
}