普通回文判断
普通的回文判断还是比较简单的。有如下几种选择:
1. 将字符串翻转,与原字符串依次比较;
2. 头尾比较,一直比较到string.length()/2的位置。
最长回文字符串判断
LeetCode 5. 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:
Input: “babad”
Output: “bab”Note: “aba” is also a valid answer.
Example:
Input: “cbbd”
Output: “bb”
虽然题目依旧叫做回文的“判断”。但是如果这里依旧循着判断的思路(即给定一个长度的字符串,判断它是不是回文)进行的话,比较难想出低复杂度的方案。因此,这里选择自行“生成”回文而不是判断的方案会更加便利。当然,“判断”方案也是有的。
自行生成的方案:
以每一个或每两个字符作为回文的中心,生成回文
private int lo, maxLen;
public String longestPalindrome(String s) {
int len = s.length();
if (len < 2)
return s;
for (int i = 0; i < len-1; i++) {
extendPalindrome(s, i, i); //assume odd length, try to extend Palindrome as possible
extendPalindrome(s, i, i+1); //assume even length.
}
return s.substring(lo, lo + maxLen);
}
private void extendPalindrome(String s, int j, int k) {
while (j >= 0 && k < s.length() && s.charAt(j) == s.charAt(k)) {
// 从里往外 一点点 构建回文
j--;
k++;
}
if (maxLen < k - j - 1) {
lo = j + 1;
maxLen = k - j - 1;
}
}
依旧采用判断的dp方案
public String longestPalindrome(String s) {
int n = s.length();
String res = null;
boolean[][] dp = new boolean[n][n];//初始化 默认值 为false
//************************************************
//*****非递归dp,外层从后往前,内层从i开始 反向*********
//************************************************
for (int i = n - 1; i >= 0; i--) {
for (int j = i; j < n; j++) {
//也算是从小范围到大范围一点点构建dp数组,判断时从外往里判断
dp[i][j] = s.charAt(i) == s.charAt(j) && (j - i < 3 || dp[i + 1][j - 1]);
if (dp[i][j] && (res == null || j - i + 1 > res.length())) {
res = s.substring(i, j + 1);
}
}
}
return res;
}
最长回文序列判断
LeetCode 516. Longest Palindromic Subsequence
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”.
乍一看比上面一题难,因为是从序列中挑选任意数量的字符(不改变相对顺序)成为回文序列。然而,仔细分析一下,会发现其满足DP的所有要素:
1. 子问题分解
从序号i
到j
内含有的最长回文子序列等价于[i+1,j] [i,j-1] [i+1,j-1]
三个相关的子问题,且都和母问题是一种性质的问题。
2. 子问题独立,存在最优子结构
上面三个问题都是独立的,并且有一个(或两个)是最优的。
3. 存在边界
当i > j
时,最长回文子序列长度为 0,当i = j
时,最长回文子序列长度为 1。
4. 备忘录
用int dp[i][j]
可以表示范围i
到j
内含有的最长回文子序列的长度。
代码如下:
public class Solution {
public int longestPalindromeSubseq(String s) {
int[][] dp = new int[s.length()][s.length()];
//************************************************
//*****非递归dp,外层从后往前,内层从i开始 反向*********
//************************************************
for (int i = s.length() - 1; i >= 0; i--) {
dp[i][i] = 1;
for (int j = i+1; j < s.length(); j++) {
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = dp[i+1][j-1] + 2;
} else {
dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);
}
}
}
return dp[0][s.length()-1];
}
}
//Top bottom recursive method with memoization
//递归的方案复杂度还是比非递归要高点
public class Solution {
public int longestPalindromeSubseq(String s) {
return helper(s, 0, s.length() - 1, new Integer[s.length()][s.length()]);
}
private int helper(String s, int i, int j, Integer[][] memo) {
if (memo[i][j] != null) {
return memo[i][j];
}
if (i > j) return 0;
if (i == j) return 1;
if (s.charAt(i) == s.charAt(j)) {
memo[i][j] = helper(s, i + 1, j - 1, memo) + 2;
} else {
memo[i][j] = Math.max(helper(s, i + 1, j, memo), helper(s, i, j - 1, memo));
}
return memo[i][j];
}
}