LeetCode-动规-最长递增子序列、最长连续递增序列、最长重复子数组、最长公共子序列、不相交的线、最大子数组和、判断子序列、不同的子序列、两个字符串的删除操作、编辑距离、回文子串、最长回文子序列

最长递增子序列

代码:

class Solution {	//300. 最长递增子序列
	//动态规划五部曲:
	//1. 确定dp数组以及下标的含义:dp[i] 从0到i的以nums[i]结尾的最长严格递增子序列的长度为dp[i]
	//2. 确定递推公式:位置i的最长升序子序列等于j从0到i-1各个位置的最长升序子序列 + 1 的最大值 if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);
	//3. dp数组如何初始化:dp[i]=1;
	//4. 确定遍历顺序:从前往后
	//5. 举例推导dp数组:nums = [10,9,2,5,3,7,101,18], dp={1,1,1,2,2,3,4,4}
public:
	int lengthOfLIS(vector<int>& nums) {
		int len = nums.size();
		if (len <= 1) return len;
		vector<int> dp(len, 1);
		int result = 0; 
		for (int i = 1; i < len; ++i) {
			for (int j = 0; j < i; ++j) {
				if (nums[i] > nums[j]) {
					dp[i] = max(dp[i], dp[j] + 1);
				}
			}
			if (dp[i] > result) result = dp[i];	//将最长的严格递增子序列保存

		}
		return result;
	}
};

int main() {
	vector<int> nums = { 4, 10, 4, 3, 8, 9 };
	Solution s;
	int result = s.lengthOfLIS(nums);
	cout << result << endl;
	return 0;
}

最长连续递增序列

代码:

class Solution {	//674. 最长连续递增序列
	//动态规划五部曲:
	//1. 确定dp数组以及下标的含义:dp[i] 下标从0-i以nums[i]结尾的最长连续递增子序列为dp[i]
	//2. 确定递推公式:if(nums[i]>nums[i-1]) dp[i]=dp[i-1]+1; else dp[i]=dp[i];
	//3. dp数组如何初始化:都为1
	//4. 确定遍历顺序:从前往后
	//5. 举例推导dp数组:nums = [1,3,5,4,7] dp={1,2,3,1,2}
public:
	int findLengthOfLCIS(vector<int>& nums) {
		int len = nums.size();
		if (len <= 1)return len;
		vector<int> dp(len, 1);
		int result = 0;
		for (int i = 1; i < len; ++i) {
			if (nums[i] > nums[i - 1]) dp[i] = dp[i - 1] + 1;
			else dp[i] = dp[i];
			if (dp[i] > result)result = dp[i];
		}
		return result;
	}
};

最长重复子数组

代码:

class Solution {	//718. 最长重复子数组
	//动态规划五部曲:
	//1. 确定dp数组以及下标的含义:dp[i][j] :以下标i - 1为结尾的nums1,和以下标j - 1为结尾的nums2,最长重复子数组长度为dp[i][j]
	//2. 确定递推公式:if(nums1[i-1]==nums2[j-1])  dp[i][j]=dp[i-1][j-1]+1; 
	//3. dp数组如何初始化:dp[i][0]  dp[0][j] 都初始化为0
	//4. 确定遍历顺序:外层for循环遍历nums1,内层for循环遍历nums2
	//5. 举例推导dp数组:
public:
	int findLength(vector<int>& nums1, vector<int>& nums2) {
		vector<vector<int>> dp(nums1.size() + 1, vector<int>(nums2.size() + 1, 0));
		int result = 0;
		for (int i = 1; i <= nums1.size(); ++i) {
			for (int j = 1; j <= nums2.size(); ++j) {
				if (nums1[i-1] == nums2[j-1]){
					dp[i][j] = dp[i - 1][j - 1] + 1;
				}
				if (dp[i][j] > result)result = dp[i][j];
			}
		}
		return result;
	}
};

最长公共子序列

代码:

class Solution {	//1143. 最长公共子序列
	//动态规划五部曲:
	//1. 确定dp数组以及下标的含义:dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j]
	//2. 确定递推公式:
	//主要就是两大情况: text1[i - 1] 与 text2[j - 1]相同,text1[i - 1] 与 text2[j - 1]不相同
	//如果text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1;
	//如果text1[i - 1] 与 text2[j - 1]不相同,那就看看text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列,取最大的。
	//	即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
	//3. dp数组如何初始化:dp[i][0]=0 dp[0][j]=0
	//4. 确定遍历顺序:从前往后,从上到下
	//5. 举例推导dp数组:
public:
	int longestCommonSubsequence(string text1, string text2) {
		vector<vector<int>> dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));
		for (int i = 1; i <= text1.size(); ++i) {
			for (int j = 1; j <= text2.size(); ++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[text1.size()][text2.size()];
	}
};

不相交的线

代码:

class Solution {	//1035. 不相交的线 本质上与求两个数组的最长公共子序列一样
public:
	int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
		vector<vector<int>> dp(nums1.size() + 1, vector<int>(nums2.size() + 1, 0));
		for (int i = 1; i <= nums1.size(); ++i) {
			for (int j = 1; j <= nums2.size(); ++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[nums1.size()][nums2.size()];
	}
};

最大子数组和

代码:

class Solution {	//53. 最大子数组和
	//动态规划五部曲:
	//1. 确定dp数组以及下标的含义:dp[i]:包括下标i之前的最大连续子序列和为dp[i]。每一个i为终点的连续最大子序列
	//2. 确定递推公式:dp[i]=max(dp[i-1]+nums[i],nums[i])
	//3. dp数组如何初始化:dp[0]=nums[0]
	//4. 确定遍历顺序:从前往后
	//5. 举例推导dp数组:nums = [-2,1,-3,4,-1,2,1,-5,4] dp={-2,1,-2,4,3,5,6,1,5}
public:
	int maxSubArray(vector<int>& nums) {
		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];
		}
		return result;
	}
};

判断子序列

双指针法代码:

class Solution {	//392. 判断子序列  双指针法
public:
	bool isSubsequence(string s, string t) {
		if (s.size() == 0)return true;
		if (t.size() == 0)return false;
		vector<bool> dp(s.size());
		int j = 0;
		for (int i = 0; i < s.size(); ++i) {
			while (j < t.size() && s[i] != t[j]) {
				j++;
			}
			if (j != t.size()){
				dp[i] = true;
				j++;
			}
			else {
				return false;
			}
		}
		return dp[s.size() - 1];
	}
};

代码:

class Solution {	//392. 判断子序列
	//动态规划五部曲:
	//1. 确定dp数组以及下标的含义:d[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]
	//2. 确定递推公式:在s和t中找到相同字符:dp[i][j]=dp[i-1][j-1]+1; 如果s[i-1]和t[j-1]不相等,那么dp[i][j]=dp[i][j-1]
	//3. dp数组如何初始化:dp[0][0]=0 dp[i][0]=0
	//4. 确定遍历顺序:从上到下 从左到右
	//5. 举例推导dp数组:
public:
	bool isSubsequence(string s, string t) {
		vector<vector<int>> dp(s.size() + 1, vector<int>(t.size() + 1, 0));
		for (int i = 1; i <= s.size(); ++i) {
			for (int j = 1; j <= t.size(); ++j) {
				if (s[i - 1] == t[j - 1])dp[i][j] = dp[i - 1][j - 1] + 1;
				else dp[i][j] = dp[i][j - 1];
			}
		}
		if (dp[s.size()][t.size()] == s.size())return true;
		return false;
	}
};

不同的子序列

代码:

class Solution {	//115. 不同的子序列
	//动态规划五部曲:
	//1. 确定dp数组以及下标的含义:dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。
	//2. 确定递推公式:当s[i - 1] 与 t[j - 1]相等时,dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
		//当s[i - 1] 与 t[j - 1]不相等时,dp[i][j]只有一部分组成,不用s[i - 1]来匹配,即递推公式为:dp[i][j] = dp[i - 1][j];
	//3. dp数组如何初始化:
		//dp[i][0] 表示:以i - 1为结尾的s可以随便删除元素,出现空字符串的个数。
		//那么dp[i][0]一定都是1,因为也就是把以i - 1为结尾的s,删除所有元素,出现空字符串的个数就是1。
		//再来看dp[0][j],dp[0][j]:空字符串s可以随便删除元素,出现以j - 1为结尾的字符串t的个数。
		//那么dp[0][j]一定都是0,s如论如何也变成不了t。
		//最后就要看一个特殊位置了,即:dp[0][0] 应该是多少。
		//dp[0][0]应该是1,空字符串s,可以删除0个元素,变成空字符串t。
	//4. 确定遍历顺序:从上到下,从左到右
	//5. 举例推导dp数组:
public:
	int numDistinct(string s, string t) {
		vector<vector<uint64_t>> dp(s.size() + 1, vector<uint64_t>(t.size() + 1));
		for (int i = 0; i <= s.size(); ++i) {
			dp[i][0] = 1;
		}
		for (int j = 1; j <= t.size(); ++j) {
			dp[0][j] = 0;
		}

		for (int i = 1; i <= s.size(); ++i) {
			for (int j = 1; j <= t.size(); ++j) {
				if (s[i - 1] == t[j - 1])dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
				else dp[i][j] = dp[i - 1][j];
			}
		}
		return dp[s.size()][t.size()];
	}
};

两个字符串的删除操作

代码:

class Solution {	//583. 两个字符串的删除操作  求最长公共子序列dp[word1.size][word2.size] 然后word1.size+word2.size-2*dp[word1.size][word2.size]
	//动态规划五部曲:
	//1. 确定dp数组以及下标的含义:dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j]
	//2. 确定递推公式:
	//主要就是两大情况: text1[i - 1] 与 text2[j - 1]相同,text1[i - 1] 与 text2[j - 1]不相同
	//如果text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1;
	//如果text1[i - 1] 与 text2[j - 1]不相同,那就看看text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列,
	//	即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
	//3. dp数组如何初始化:dp[i][0]=0 dp[0][j]=0
	//4. 确定遍历顺序:从前往后,从上到下
	//5. 举例推导dp数组:
public:
	int minDistance(string word1, string word2) {
		vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));
		for (int i = 1; i <= word1.size(); ++i) {
			for (int j = 1; j <= word2.size(); ++j) {
				if (word1[i - 1] == word2[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 word1.size() + word2.size() - 2 * dp[word1.size()][word2.size()];
	}
};

编辑距离

代码:

class Solution {	//72. 编辑距离
	//动态规划五部曲:
	//1. 确定dp数组以及下标的含义:dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]。
	//2. 确定递推公式:
		//if (word1[i - 1] == word2[j - 1]) {	不用操作
		//	dp[i][j] = dp[i - 1][j - 1];
		//}
		//else {
		//	dp[i][j] = min({ dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1] }) + 1; dp[i - 1][j - 1]+1表示替换,dp[i - 1][j]+1表示word1删除一个元素  dp[i][j - 1]表示word2删除一个元素
		//}
	//3. dp数组如何初始化:dp[i][0]=i;dp[0[j]=j;
	//4. 确定遍历顺序:从左到右,从上到下
	//5. 举例推导dp数组:
public:
	int minDistance(string word1, string word2) {
		vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1));
		for (int i = 0; i <= word1.size(); ++i)dp[i][0] = i;
		for (int j = 0; j <= word2.size(); ++j)dp[0][j] = j;
		for (int i = 1; i <= word1.size(); ++i) {
			for (int j = 1; j <= word2.size(); ++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 - 1], dp[i][j - 1], dp[i - 1][j] }) + 1;
			}
		}
		return dp[word1.size()][word2.size()];
	}
};

回文子串

代码:

class Solution {	//647. 回文子串
	//动态规划五部曲:
	//1. 确定dp数组以及下标的含义:布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]为true,否则为false。
	//2. 确定递推公式:如果s[i]与s[j]不相等,dp[i][j]为false。如果s[i]与s[j]相等,有三种情况,一是i=j,那么dp[i][j]=true;二是i=j-1,dp[i][j]=true;三是相差大于1,那么dp[i][j]=dp[i+1][j-1]
	//3. dp数组如何初始化:全为false
	//4. 确定遍历顺序:dp[i][j]依赖于dp[i+1][j-1] 所以从下到上,从左到右遍历
	//5. 举例推导dp数组:
public:
	int countSubstrings(string s) {
		int len = s.size();
		int result = 0;
		vector<vector<bool>> dp(len, vector<bool>(len, false));
		for (int i = len - 1; i >= 0; --i) {
			for (int j = i; j < len; ++j) {
				if (s[i] == s[j]) {
					if (j - i <= 1) {
						result++;
						dp[i][j] = true;
					}
					else if(dp[i+1][j-1]) {
						result++;
						dp[i][j] = true;
					}
				}
			}
		}
		return result;
	}
};

最长回文子序列

代码:

class Solution {	//516. 最长回文子序列
	//动态规划五部曲:
	//1. 确定dp数组以及下标的含义:dp[i][j] 表示区间[i,j]的最长回文子序列的长度
	//2. 确定递推公式:如果s[i]与s[j]不等,dp[i][j]=max(dp[i+1][j],dp[i][j-1])
		//如果s[i]等于s[j],j-i<=1,dp[i][j]=j-i+1, j-i>1 dp[i][j]=dp[i+1][j-1]+2;
	//3. dp数组如何初始化:都为0
	//4. 确定遍历顺序:dp[i][j]依赖 dp[i+1][j],dp[i][j-1],dp[i+1][j-1] 从下到上,从左到右
	//5. 举例推导dp数组:
public:
	int longestPalindromeSubseq(string s) {
		int len = s.size();
		vector<vector<int>> dp(len, vector<int>(len, 0));
		for (int i = len - 1; i >= 0; --i) {
			for (int j = i; j < len; ++j) {
				if (s[i] != s[j]) {
					dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
				}
				else {
					if (j - i <= 1) {
						dp[i][j] = j - i + 1;
					}
					else {
						dp[i][j] = dp[i + 1][j - 1] + 2;
					}
				}
			}
		}
		return dp[0][len - 1];
	}
};

参考资料:

代码随想录

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海螺蜜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值