动态规划——子序列解题模板
1、一维的 dp 数组
模板:
int n = array.length;
int[] dp = new int[n];
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
dp[i] = 最值(dp[i], dp[j] + ...)
}
}
dp[i]的含义: 在子数组array[0…i]中,以array[i]结尾的目标子序列的长度是dp[i]。(类似如此)
例子:最长递增子序列
问题描述
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
解题思路
定义dp[i] 表示以 nums[i] 这个数结尾的最长递增子序列的长度,那么要求dp[i],则需要先求出max(dp[1],dp[2],…,dp[i-1]),假设max(dp[1],dp[2],…,dp[i-1]) = dp[j] , 则如果nums[i] > nums[j] ,则dp[i] = dp[j] + 1,否则dp[i]保持原值不变。
代码实现
class Solution {
public int lengthOfLIS(int[] nums) {
int n = nums.length;//求出数组元素个数
if(n == 0) //若数组为空,返回0
return 0;
int dp[] = new int[n];//dp[i]表示以nums[i]结尾的最大递增子序列长度
int result = -1;
for(int i=0;i<n;i++){
dp[i] = 1; //初始都为1
for(int j=0;j<i;j++){
//dp[0]开始
if(nums[i]>nums[j])//若nums[i]>nums[j]则dp[i]可以为dp[j]+1,否则对dp[i]不做处理
dp[i] = Math.max(dp[i],dp[j]+1);
}
result = Math.max(dp[i],result);
}
return result;
}
}
时间复杂度:O(n^2)
空间复杂度:O(n)
2、二维的 dp 数组
模板:
int n = arr.length;
int[][] dp = new dp[n][n];
for (int i = 0; i < n; i++) {
for (int j = 1; j < n; j++) {
if (arr[i] == arr[j])
dp[i][j] = dp[i][j] + ...
else
dp[i][j] = 最值(...)
}
}
dp[i][j]的含义:
- 涉及两个字符串/数组时:在子数组arr1[0…i]和子数组arr2[0…j]中,我们要求的子序列长度为dp[i][j]。例子:编辑距离、最长公共子序列…
- 只涉及一个字符串/数组时:在子数组array[i…j]中,我们要求的子序列的长度为dp[i][j]。例子:最长回文子序列…
例子:最长回文子序列
问题描述
给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s 的最大长度为 1000 。
解题思路
定义dp[i][j]为子串s[i,…,j]中的最长回文子序列长度,假设此时已知dp[i+1][j-1],则若s[i]==s[j],则dp[i][j] = dp[i+1][j-1] + 2;否则等于max(dp[i+1][j],dp[i][j-1])。
代码实现
class Solution {
public int longestPalindromeSubseq(String s) {
int n = s.length();
if(n == 0) return 0;
int dp[][] = new int[n][n];
for(int i=n-1;i>=0;i--){
dp[i][i] = 1;// i==j时回文串长度为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]);
}
}
return dp[0][n-1];
}
}