516. 最长回文子序列
思路:
这一题思路比较巧妙,记录一下。设dp[i][j]表示s[i:j]中的最长回文子序列长度,则
(1) s[i] == s[j] ==> dp[i][j] = dp[i+1][j-1] + 2;
(2) s[i] != s[j] ==> dp[i][j] = max(dp[i+1][j], dp[i][j-1])
初始化: dp[i][i] = 1
由i,j的更新方式知道i应该逆序,j应该正序,且j应该比i大:
for (int i = n - 1; i >= 0; i--) {
dp[i][i] = 1;
for (int j = i + 1; j < n; 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]);
}
完整代码如下:
public int longestPalindromeSubseq(String s) {
if (s == null || s.length() == 0) return 0;
int n = s.length();
int[][] dp = new int[n][n];
for (int i = n - 1; i >= 0; i--) {
dp[i][i] = 1;
for (int j = i + 1; j < n; j++) {//i+1不会越界,因为j = i + 1; j < n
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][n - 1];
}
当然还可以进行空间优化,但是就需要另外一个数组保存上一轮的结果,最开始我是这样写的:
public int longestPalindromeSubseq(String s) {
if (s == null || s.length() == 0) return 0;
int n = s.length();
int[] pre = new int[n];
int[] cur = new int[n];
for (int i = n - 1; i >= 0; i--) {
cur[i] = 1;
for (int j = i + 1; j < n; j++) {
if (s.charAt(i) == s.charAt(j)) {
cur[j] = pre[j - 1] + 2;
} else {
cur[j] = Math.max(pre[j], cur[j - 1]);
}
}
for(int p=0;p<n;p++) pre[p] = cur[p];
//pre = cur;
cur = new int[n];
}
return pre[n - 1];
}
因为pre这个数组保存上一层遍历结果,所以内循环结束就得更新一次,即把cur赋值给pre,注意这里必须是复制整个数组,而不是只传递引用,所以我这样写
for(int p=0;p<n;p++) pre[p] = cur[p];
然后再清空cur数组
cur = new int[n];
这样写运行完全正确,因为我知道如果直接传递pre = cur则修改pre的同时cur也会被修改!于是我想证明我是错的,于是我就写成这样
//for(int p=0;p<n;p++) pre[p] = cur[p];
pre = cur;
cur = new int[n];
然后又跑了一遍,发现也是对的!!!为什么呢???难道数组的等号赋值传递的不是引用吗?当然是!但是这里问题不在这里,我举个例子:
int[] array = new int[5];
int[] temp = new int[5];
array[0] = 1;
temp[0] = 2;
temp = array;
System.out.println(temp[0]);
array[0] = 3;
//array = new int[5];
System.out.println(temp[0]);
这段代码的输出是1,3,很显然是因为传递引用后array被修改所以temp也被修改,接下来看下一段:
int[] array = new int[5];
int[] temp = new int[5];
array[0] = 1;
temp[0] = 2;
temp = array;
System.out.println(temp[0]);
//array[0] = 3;
array = new int[5];
System.out.println(temp[0]);
这里输出是1,0吗?不是!!!是1,1,为什么呢,问题就在于array = new int[5];也就是array重新指向了另一个数组,然而之前存在于内存中的数组【1,0,0,0,0】还是存在的,被temp指向,这时两个数组各自修改都不会影响对方(而之前两个引用指向统一内存位置)!!!!
5. 最长回文子串
思路:
这一题和上一题类似,但是字符串必须是连续的,所以dp数组就不可以存长度了,应该改为布尔数组:
public String longestPalindrome(String s) {
boolean[][] dp = new boolean[s.length()][s.length()];
for(int i=0;i<s.length();i++) dp[i][i] = true;
int max = 0;
int start = 0;
int end = 0;
for(int i=s.length()-1;i>=0;i--){
for(int j=i+1;j<s.length();j++){
if(s.charAt(i) == s.charAt(j)){
if(j - i < 3){//这里需要注意
dp[i][j] = true;
}else{
dp[i][j] = dp[i+1][j-1];
}
}else{
dp[i][j] = false;
}
if(dp[i][j] && j - i + 1 > max){
start = i;
end = j;
max = j - i + 1;
}
}
}
return s.substring(start,end+1);
}
也可以进行空间优化,后续再补充。。。