子序列就是在一个数组中按顺序挑选若干数,子数组是连续的子序列,视为特殊的子序列问题
子序列
最长递增子序列
300. 最长递增子序列 - 力扣(LeetCode)https://leetcode.cn/problems/longest-increasing-subsequence/description/
- dp[i]表示以第i个数为结尾的,前i个数中最长严格递增子序列的长度。
- 递推得遍历之前所有的位置,并且满足第i个位置比第j个位置大,才能保证正确
- 初始化第0个位置为1
- 遍历顺序从0到size-1
- 举例略
代码实现
class Solution {
public:
//dp[i]是以nums[i]结尾的最长严格递增子序列的长度
int lengthOfLIS(vector<int>& nums) {
int n=nums.size();
vector<int> dp(n,1);
int max1=1;
for(int i=1;i<n;i++)
{
for(int j=0;j<i;j++)
{
if(nums[i] > nums[j])
{
dp[i]=max(dp[i],dp[j]+1);
max1=max(max1,dp[i]);
}
}
}
return max1;
}
};
最长连续递增序列(最长递增子数组)
- dp[i]的含义是以第i个数为结尾,前i个数中最长的递增子序列长度
- 做个判断,如果nums[i]>nums[i-1],则dp[i]=dp[i-1]+1
- 全都初始化为1
- 遍历从0到nums.size()-1
- 举例略
代码实现
class Solution {
public:
//dp[j] 表示以j结尾的连续递增子序列长度
//递推公式
int findLengthOfLCIS(vector<int>& nums) {
int n=nums.size();
int maxAns=1;
vector<int> dp(n,1);
for(int i=1;i<n;i++)
{
if(nums[i]>nums[i-1]) {
dp[i]=dp[i-1]+1;
if(dp[i]>maxAns) maxAns=dp[i];
}
}
return maxAns;
}
};
附一个双指针实现
class Solution {
public:
//dp[j] 表示以j结尾的连续递增子序列长度
//递推公式
int findLengthOfLCIS(vector<int>& nums) {
int n=nums.size();
int l,r=1;
int maxlen=1;
for(l=0;l<n;l++)
{
while(r<n&&nums[r]>nums[r-1])
{
maxlen=max(maxlen,r-l+1);
r++;
}
//这个重置状态你得想一下
l=r-1;r=r+1;
}
return maxlen;
}
};
最长公共子数组
- dp[i][j] 表示nums1中以i结尾,nums2中以j结尾的最长子数组长度
- 递推的时候如果nums1[i]=nums2[j] dp[i][j]=dp[i-1][j-1]+1,否则dp[i][j]=0;
- 初始化都等于0
- 遍历的时候都从0到size
- 举例略
代码实现
class Solution {
public:
int findLength(vector<int>& nums1, vector<int>& nums2) {
//子数组是连续子序列
int sizeA=nums1.size();
int sizeB=nums2.size();
int max=0;
//dp[i][j]的含义是nums1第i个数结尾的子数组和nums2第j个数结束的子数组公共的最大长度
vector<vector<int>> dp(sizeA+1,vector<int>(sizeB+1,0));
for(int i=1;i<=sizeA;i++)
{
for(int j=1;j<=sizeB;j++)
{
if(nums1[i-1]==nums2[j-1])
{
dp[i][j]=dp[i-1][j-1]+1;
if(dp[i][j]>=max)
{
max=dp[i][j];
}
}
}
}
return max;
}
};
最长公共子序列
1143. 最长公共子序列 - 力扣(LeetCode)https://leetcode.cn/problems/longest-common-subsequence/description/
- dp[i][j]表示nums1以第i个数结尾,nums2以第j个数结尾的最长公共子序列长度
- 递推公式,如果第i个数字和第j个数字想等,dp[i][j]=dp[i-1][j-1]+1;如果不相等,则dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
- 初始化,都初始化为0即可
- 遍历,从0到size
- 举例略
代码实现
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
//dp[i][j]表示以text1以i-1结尾,text2以j-1结尾的公共子序列最长长度
int size1=text1.size();
int size2=text2.size();
vector<vector<int>>dp(size1+1,vector<int>(size2+1,0));
for(int i=1;i<=size1;i++)
{
for(int j=1;j<=size2;j++)
{
if(text1[i-1]==text2[j-1])
{
dp[i][j]=dp[i-1][j-1]+1;
}
else
{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
}
return dp[size1][size2];
}
};
不相交的线
1035. 不相交的线 - 力扣(LeetCode)https://leetcode.cn/problems/uncrossed-lines/description/这题实际上就是求一个最长公共子序列
代码实现
class Solution {
public:
int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
int size1=nums1.size(),size2=nums2.size();
vector<vector<int>>dp(size1+1,vector<int>(size2+1,0));
for(int i=1;i<=size1;i++)
{
for(int j=1;j<=size2;j++)
{
if(nums1[i-1]==nums2[j-1])
{
dp[i][j]=dp[i-1][j-1]+1;
}
else
{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
}
return dp[size1][size2];
}
};
最大子数组和
53. 最大子数组和 - 力扣(LeetCode)https://leetcode.cn/problems/maximum-subarray/
- dp[i]表示以i结尾的最大子数组和
- 递推公式,如果dp[i-1]>0,则dp[i]=dp[i-1]+nums[i];否则自己开山,dp[i]=nums[i]
- 初始化dp[0]=num[0];
- 递推从0到n
- 举例略
代码实现
class Solution {
public:
int maxSubArray(vector<int>& nums) {
if (nums.size() == 0) return 0;
vector<int> dp(nums.size());
dp[0] = nums[0];
int result = dp[0];
for (int i = 1; i < nums.size(); i++) {
dp[i] = max(dp[i - 1] + nums[i], nums[i]); // 状态转移公式
if (dp[i] > result) result = dp[i]; // result 保存dp[i]的最大值
}
return result;
}
};
判断子序列
392. 判断子序列 - 力扣(LeetCode)https://leetcode.cn/problems/is-subsequence/description/
双指针解法
class Solution {
public:
bool isSubsequence(string s, string t) {
int n = s.length(), m = t.length();
int i = 0, j = 0;
while (i < n && j < m) {
if (s[i] == t[j]) {
i++;
}
j++;
}
return i == n;
}
};
动态规划解法
判断最长公共子序列长度和t的大小是否相等
class Solution {
public:
bool isSubsequence(string s, string t) {
//实际上就是最长公共子序列,如果这个最长公共子序列的长度是s的长度,则是
int n = s.length(), m = t.length();
//dp[i][j] 表示以j结尾的子数组 是以i结尾的字串,相同子序列的长度
vector<vector<int>> dp(n+1,vector<int>(m+1,false));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(s[i-1]==t[j-1])
{
dp[i][j]=dp[i-1][j-1]+1;
}
else
{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
}
//最长公共子序列dp[i-1][j],dp[i][j-1]
return dp[n][m]==s.size();
}
};
使用编辑的思想去做
class Solution {
public:
bool isSubsequence(string s, string t) {
int len1=s.size();
int len2=t.size();
vector<vector<bool>> dp(len1+1,vector<bool>(len2+1));
//s是待匹配,j是大的
//dp[i][j]的含义是s以i结尾的字符串是不是t以j结尾的字符串的字串
for(int j=0;j<=len2;j++)
{
dp[0][j]=1;
}
for(int i=1;i<=len1;i++)
{
for(int j=1;j<=len2;j++)
{
if(s[i-1]==t[j-1])
{
dp[i][j]=dp[i-1][j-1]||dp[i][j-1];
}
else
{
dp[i][j]=dp[i][j-1];
}
}
}
return dp[len1][len2];
}
};
两个字符串的删除操作
583. 两个字符串的删除操作 - 力扣(LeetCode)https://leetcode.cn/problems/delete-operation-for-two-strings/
代码实现
class Solution {
public:
int minDistance(string word1, string word2)
{
//dp[i][j]表示word1以i结尾和word2以j结尾的最小步数
int len1=word1.size(),len2=word2.size();
vector<vector<int>> dp(len1+1,vector<int>(len2+1));
for(int i=0;i<=len1;i++)
{
dp[i][0]=i;
}
for(int j=0;j<=len2;j++)
{
dp[0][j]=j;
}
for(int i=1;i<=len1;i++)
{
for(int j=1;j<=len2;j++)
{
if(word1[i-1]==word2[j-1])
{
dp[i][j]=dp[i-1][j-1];
}
else
{
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+1;
}
}
}
return dp[len1][len2];
}
};
编辑距离
72. 编辑距离 - 力扣(LeetCode)https://leetcode.cn/problems/edit-distance/
代码实现
class Solution {
public:
int minDistance(string word1, string word2) {
//dp[i][j]的含义是把word1以i结尾的子串转化成word2以j结尾的字串的最少操作数
int len1=word1.size();
int len2=word2.size();
vector<vector<int>>dp(len1+1,vector<int>(len2+1,0));
for(int i=0;i<=len1;i++)
{
dp[i][0]=i;
}
for(int j=0;j<=len2;j++)
{
dp[0][j]=j;
}
for(int i=1;i<=len1;i++)
{
for(int j=1;j<=len2;j++)
{
if(word1[i-1]==word2[j-1])
{
dp[i][j]=dp[i-1][j-1];
}
else
{
dp[i][j]=min(dp[i-1][j],dp[i][j-1]);
dp[i][j]=min(dp[i][j],dp[i-1][j-1]);
dp[i][j]+=1;
}
}
}
return dp[len1][len2];
}
};