前言
首先子串!=子序列
- 子串:需要连续,如{abdgfr}的子串有abd,dgfr等
- 子序列:不需要连续,只需要保持元素间相对有序。如 {abdgfr}的子序列有adf,bgf等
即子串等于连续子序列。
我们常见的子串或子序列问题有最大子串,最长递增子序列,最长公共子串,最长公共子序列问题等。这些问题均可以用动态规划来解答,解答分为3步:
- 定义状态
- 找到状态转移方程
- 从所有状态中筛选答案
而子串与子序列问题状态定义均类似,用dp[i]定义为以i元素结尾的状态。实际上只要定义好状态,状态转移方程就很明显了。下面分别来说明:
最大子串问题
最大子串问题指的是数组中和最大的连续子序列。
题目参考:leedcode-53
- 定义状态:dp[i]表示以第i个元素结尾的最大子串
- 状态转移:dp[i]=max(dp[i-1]+num[i-1], num[i-1])
- 数组的最大子串和为max(dp[i])
int maxSubArray(vector<int>& nums) {
int res=INT_MIN;
vector<int> dp(nums.size()+1,0);
for(int i=1;i<=nums.size();i++)
{
dp[i]=max(dp[i-1]+nums[i-1],nums[i-1]);
res=max(res,dp[i]);
}
return res;
}
由于每次状态转移只用到了上一次的状态,所以其实只需要用一个变量curMax来保存每次的状态。
int maxSubArray(vector<int>& nums) {
int res=INT_MIN;
int curMax=0;
for(int i=0;i<nums.size();i++)
{
curMax=max(curMax+nums[i],nums[i]);
res=max(res,curMax);
}
return res;
}
最长递增子序列问题
最长递增子序列又称LIS(Longest Increasing Subsequence),顾名思义为求最长的递增的子序列。
题目参考:leedcode-300
- 定义状态:dp[i]表示num[i]结尾的最长递增子序列的长度
- 状态转移:
- dp[i]=max(dp[j]+1) { j<i && num[i]>num[j] }
- or dp[i]=1(此时它比前面元素都小,因此只有它本身)
- 最长递增序列长度为max(dp[i])
int lengthOfLIS(vector<int>& nums) {
int res = 0;
int n = nums.size();
vector<int>dp(n, 1);
for(int i = 0 ;i<n;i++){
for(int j=0;j<i;j++){
if(nums[i]>nums[j]){
dp[i] = max(dp[j]+1, dp[i]);
}
}
res = max(dp[i], res);
}
return res;
}
最长公共子串问题
求两个数组S1,S2的最长的公共子串。
题目参考:leedcode-718
- 定义状态:dp[i][j]表示以S1第i个元素结尾且以S2第j个元素结尾的最长公共子串
- 状态转移:
- 当S1[i]==S2[j]时,dp[i][j]==dp[i-1][j-1]+1
- 当S1[i]!=S2[j]时,dp[i][j]==0
- 最长公共子串长度为max( dp[i][j] )
int findLength(vector<int>& A, vector<int>& B) {
int n=A.size(),m=B.size();
vector<vector<int>> dp(n+1,vector<int>(m+1,0));
int res=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(A[i-1]==B[j-1]) dp[i][j]=dp[i-1][j-1]+1;
else dp[i][j]=0;
res=max(res,dp[i][j]);
}
}
return res;
}
最长公共子序列问题
简称为LCS(Longest Common Subsequence)问题, 如求两个数组S1,S2的最长的公共的子序列。
题目参考:leedcode-583
- 定义状态:dp[i][j]表示S1前i个元素与S2前j个元素的最长公共子序列(不一定需要第i/j个元素)
- 状态转移:
- 当S1[i]==S2[j],dp[i][j]==dp[i-1][j-1]+1
- 当S1[i]!=S2[j],dp[i][j]=max(dp[i][j-1],dp[i-1][j])
- 最大长度为dp[n][m]
int lengthOfLCS(string S1,string S2){
int n=S1.size();
int m=S2.size();
vector<vector<int>> dp(n+1,vector<int>(m+1,0);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(S1[i-1]==S2[j-1]){
dp[i][j]=dp[i-1][j-1]+1;
}else{
dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
}
}
}
return dp[n][m];
}