回文子串是要连续的,回文子序列可不是连续的
516. 最长回文子序列
解决本题一个很妙的做法就是,设置一个二维数组,行为给定字符串的最后一位,列每次的起始值为行+1。
这样做的好处,类似于倒着来。因为当前状态由前面的状态得来。而最终要求的是起始值为0,终值为n-1的那个位置的值。
for(int i = n - 1; i >= 0; i--) {
ans[i][i] = 1;
char a = s.charAt(i);
for(int j = i + 1; j < n; j++) {
char b = s.charAt(j);
if(a == b) {
ans[i][j] = ans[i + 1][j - 1] + 2;
}else {
ans[i][j] = Math.max(ans[i + 1][j], ans[i][j - 1]);
}
}
}
return ans[0][n - 1];
416. 分割等和子集
由题意可得,如果所有数之和为奇数,那么可以直接返回false,如果数组中最大值大于所有数之和的一半,也可直接返回false。
设置一个二维数组,第一个数字表示取出数组中哪些数字。第二个数字表示这些数字需要达到的和的值。
那么初始化,如果当前需要达到的值的和为零的话,只要不取数字即可,所以都可置为true。
只有第零个数字的时候,只能达到nums[0]的值,所以ans[0][nums[0]] = true。
之后的遍历中,如果取到的当前数字小于或者等于所需达到的和的值,可以选择要当前数字或者不要:
ans[i][j] = ans[i - 1][j] | ans[i - 1][j - num];//没选 | 选
否则:
ans[i][j] = ans[i - 1][j]
1143. 最长公共子序列 & 718. 最长重复子数组
3 2 1 4 7 0 0 0 0 0 0
1 0 0 1 0 0 0 0 0 0 0 0
2 0 1 0 0 0 0 0 0 0 0 0
3 1 0 0 0 0 0 0 0 0 0 0
2 0 2 0 0 0 0 0 0 0 0 0
1 0 0 3 0 0 0 0 0 0 0 0
解决这两道题的一个【小技巧】将二维数组多一行多一列
这两道题,最大的区别是,前者求公共子序列(不需要连着),后者求数组(需要连着)。
所以前者在当前位置不满足条件的时候,依旧要改变推移其状态,而后者不需要。
前者:
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(text1.charAt(i - 1) == text2.charAt(j - 1)) {
ans[i][j] = ans[i - 1][j - 1] + 1;
}else {
ans[i][j] = Math.max(ans[i - 1][j], ans[i][j - 1]);//变化
}
}
}
后者:
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(nums1[i - 1] == nums2[j - 1]) {
ans[i][j] = ans[i - 1][j - 1] + 1;
}
max = Math.max(max, ans[i][j]);
}
}