图片来源:代码随想录https://leetcode-cn.com/problems/longest-palindromic-subsequence/solution/516-zui-chang-hui-wen-zi-xu-lie-dong-tai-hap0/
##「代码随想录」带你学透DP子序列问题!516. 最长回文子序列【动态规划】详解
var lengthOfLIS = function (nums) {
const len = nums.length;
let dp = new Array(len).fill(1);
for (let i = 1; i < len; i++) {
for (let j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
}
return Math.max.apply(null, dp);
};
let arr = [1, 3, 6, 7, 9, 4, 10, 5, 6];
lengthOfLIS(arr)
var findLengthOfLCIS = function(nums) {
// dp[i] 表示以 i结尾最长连续递增序列的长度
// base case dp[0] = 1
// dp[i] = dp[i-1]+1 如果 nums[i-1]>nums[i]
const dp = new Array(nums.length).fill(1);
for(let i =1;i<nums.length;i++){
if(nums[i-1]<nums[i]){
dp[i]=dp[i-1]+1;
}
}
return Math.max.apply(null,dp);
};
定义dp:
length[i]以i结尾的最长递增序列的长度
count[i]以i结尾的最长递增序列的个数
初始值:
默认length[i] 最长长度,长度就是它自己,长度为1
默认count[i] 最长个数,个数默认1
const len = nums.length;
let length = new Array(len).fill(1); // length[i]以i结尾的最长递增序列的长度
let count = new Array(len).fill(1); // count[i]以i结尾的最长递增序列的个数
maxFun = () => {
const max = 0;
for (let i = 1; i < len; i++) {
for (let j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
if (length[j] >= length[i]) { // 前一个最大长度大于等于目标位置长度
length[i] = length[j] + 1;
count[i] = count[j];
} else if (length[j] + 1 == length[i]) {
count[i] += count[j];
}
}
}
}
return Math.max.apply(null, length)
}
let Maxlen = maxFun(nums), rs = 0;
for (let i = 0, j = 0; i < length.length; i++) {
if (length[i] == Maxlen) {
rs += count[i];
}
}
return rs
var findLength = function(nums1, nums2) {
const len1 = nums1.length, len2 = nums2.length;
let dp = new Array(len1), max = 0;
for (let i = 0; i < len1; i++) {
dp[i] = new Array(len2).fill(0);
}
for (let i = 0; i < len1; i++) {
for (let j = 0; j < len2; j++) {
if (nums1[i] == nums2[j]) {
if (i == 0 || j == 0) {
dp[i][j] = 1;
} else {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
if (dp[i][j] > max) {
max = dp[i][j]
}
}
}
}
return max;
};
var longestCommonSubsequence = function(text1, text2) {
let dp = new Array(text1.length+1);
for(let i=0;i<dp.length;i++){
dp[i] = new Array(text2.length+1).fill(0);
}
for(let i=1;i<text1.length+1;i++){
for(let j=1;j<text2.length+1;j++){
if(text1[i-1]===text2[j-1]){
dp[i][j] = dp[i-1][j-1]+1
}else{
dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1])
}
}
}
return dp[text1.length][text2.length]
};
// 动态规划 O(N^2)+O(N^2)
var countSubstrings = function (s) {
const len = s.length;
let rs = 0;
const dp = new Array(len);
for (let i = 0; i < dp.length; i++) {
dp[i] = new Array(len).fill(false);
}
for (let j = 0; j < len; j++) { // 从上往下,从左往右
for (let i = 0; i <= j; i++) {
if (s[i] === s[j]) {
if (j - i < 2) { // 单个字母或者两个相同字母时
dp[i][j] = true;
rs++;
} else if (dp[i + 1][j - 1]) { // 三个以上是回文字符串
dp[i][j] = true
rs++;
}
}
}
}
return rs;
};
// 二分法 中心扩散 (数组中 每个元素就是一个中心(可以是一个元素,也可以是两个相同元素))
// O(N^2)+O(1)
var countSubstrings2 = function (s) {
let rs = 0;
// 已s的每一个元素为中心;
for (let i = 0; i < s.length; i++) {
rs += extend(i, i, s); // 选择一个中心
rs += extend(i, i + 1, s); // 选择两个中心
}
function extend(i, j, s) { // 已中心朝外扩散,看有多少个回文串。
let res = 0;
while (i >= 0 && j < s.length && s[i] == s[j]) {
i--;
j++;
res++;
}
return res;
}
return rs;
};
var longestPalindrome = function (s) {
const len = s.length;
let max = 0, rs = "";
const dp = new Array(len);
for (let i = 0; i < len; i++) {
dp[i] = new Array(len).fill(false);
}
for (let j = 0; j < len; j++) {
for (let i = 0; i <= j; i++) {
if (s[i] == s[j]) {
if (j - i < 2) { // 等于长度1或2的回文串
dp[i][j] = true;
} else if (dp[i + 1][j - 1]) { // 长度为3的回文串
dp[i][j] = true;
}
if (dp[i][j] && max < j - i + 1) { //求最值
max = j - i + 1;
rs = s.slice(i, j + 1);
}
}
}
}
return rs;
};
// 双指针 中心扩散
var longestPalindrome1 = function (s) {
const len = s.length;
let rs = '', max = 0;
for (let i = 0; i < len; i++) {
// 返回当前较长的回文串
let tmp = Math.max(extend(i, i, s).length, extend(i, i + 1, s).length);
// 记录给max
max = max < tmp ? tmp : max;
// max等于谁,在把结果给谁
rs = max == extend(i, i, s).length ? extend(i, i, s) : rs
rs = max == extend(i, i + 1, s).length ? extend(i, i + 1, s) : rs
}
function extend(i, j, s) {
let res = '';
while (i >= 0 && j < len && s[i] == s[j]) {
res = s.slice(i, j + 1);
i--;
j++;
}
return res;
}
return rs;
}
最长回文子序列 ,因为回文串要比较两端的对称性,这里的dp数组应该考虑是二维数组。
s[i,j] 表示以【i,j】为范围的s字符串。dp[i][j] 表示长度范围是s[i,j] 时,最长回文子序列的长度。base case是斜对角线上的单个字母组成的回文串,长度为1。
状态转移方程:
dp[i][j] = dp[i+1][j-1] s[i]==s[j];
dp[i][j] = Math.max(dp[i][j-1],dp[i-1][j]) s[i]!=s[j]
var longestPalindromeSubseq = function(s) {
const dp = new Array(s.length), len = s.length;
for (let i = 0; i < len; i++) {
dp[i] = new Array(len).fill(0);
dp[i][i] = 1;
};
// dp[i][j] = dp[i+1][j-1] s[i]==s[j];
// dp[i][j] = Math.max(dp[i][j-1],dp[i-1][j]) s[i]!=s[j]
for (let i = len - 2; i >=0; i--) {
for (let j = i + 1; j < len; j++) {
if (s[i] == s[j]) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = Math.max(dp[i][j - 1], dp[i+1][j]);
}
}
}
return dp[0][len-1];
};