代码随想录算法训练营Day52 | 300.最长递增子序列、674.最长连续递增序列、718.最长重复子数组

300.最长递增子序列

这题的重点是DP数组的定义,子序列必须以nums[i]为最后一个元素,这样dp数组中后面的元素才能与前面的元素进行对比

1、DP数组定义 :dp[i]表示以nums[i]为最后一个元素的最长递增子序列长度

2、DP数组初始化:全部初始化为1(子序列最少也有自身一个)

3、递推公式 :++与 i 前所有元素进行对比++,如果nums[i] > nums[j],那么更新dp[i]

· 基本------dp[j]:位置 j 处的最长递增子序列

· 新增------ +1:算上nums[i],多了一个递增元素

· 最后的递推公式:dp[i]取较大值:dp[i] = std::max(dp[i], dp[j] + 1)

4、遍历顺序:从前向后遍历

cpp 复制代码
int lengthOfLIS(vector<int>& nums) {
	// dp[i]表示以nums[i]为最后一个元素的最长递增子序列长度
	// 全部初始化为1(子序列最少也有自身一个)
	vector<int> dp(nums.size(), 1);

	int ans = 1;
	for (int i = 1; i < nums.size(); ++i) {
		// 与i之前的所有元素做比较
		for (int j = 0; j < i; ++j) {
			// 不断更新dp[i],寻找以nums[i]为最后一个元素的最长递增子序列长度
			if (nums[i] > nums[j])
				dp[i] = std::max(dp[i], dp[j] + 1);
		}
		// 记录过程中的最长子序列
		if (dp[i] > ans)
			ans = dp[i];
	}
	return ans;
}

674.最长连续递增序列

整体和上一题差不多,但由于要求是"连续"子序列,所以简单不少。主要差别在遍历过程中,为了保持序列连续,只需要与前一个元素对比 即可(上一题需要与前面所有元素对比)。

cpp 复制代码
int findLengthOfLCIS(vector<int>& nums) {
	vector<int> dp(nums.size(), 1);

	int ans = 1;
	for (int i = 1; i < nums.size(); ++i) {
		// 只需要与 i - 1 比较
		if (nums[i] > nums[i - 1]) {
			dp[i] = dp[i - 1] + 1;
			ans = std::max(ans, dp[i]);
		}
	}
	return ans;
}


// 压缩空间写法
iint findLengthOfLCIS(vector<int>& nums) {
	int dp = 1;
	int ans = 1;
	for (int i = 1; i < nums.size(); ++i) {
		if (nums[i] > nums[i - 1])
			ans = std::max(ans, ++dp);
		else
			dp = 1;
	}
	return ans;
}

718.最长重复子数组

写暴力超时了,剪剪枝可能有机会过?

cpp 复制代码
int findLength0(vector<int>& nums1, vector<int>& nums2) {
	// 尝试用哈希表来加快索引
	// key:值
	// value:出现值的下标
	unordered_map<int, vector<int>> mapNum2;
	for (int i = 0; i < nums2.size(); ++i) {
		auto it = mapNum2.find(nums2[i]);
		if (it == mapNum2.end())
			mapNum2.insert({ nums2[i], {i} });
		else
			it->second.push_back(i);
	}

	vector<int> dp(nums1.size(), 0);
	int ans = 0;
	// 暴力两层循环 + 最内层判断重复子序列长度
	for (int i = 0; i < nums1.size(); ++i) {
		auto it = mapNum2.find(nums1[i]);
		if (it == mapNum2.end())
			continue;
		ans = std::max(ans, 1);
		for (int k : it->second) {
			int len = 1;
			for (int j = 1; i + j < nums1.size() && k + j < nums2.size(); ++j) {
				if (nums1[i + j] == nums2[k + j]) {
					dp[i + j] = std::max(dp[i + j], ++len);
					ans = std::max(ans, dp[i + j]);
				}
				else
					break;
			}
		}
	}
	return ans;
}

动规写法:

这题重点也是DP数组的定义:两个序列所以DP数组用二维

1、DP数组定义 :两个维度表示两个数组的索引,dp[i][j]表示以nums1[i - 1] 和**nums2[j - 1]**为结尾的两个字符串的最长重复子数组长度

(子序列问题一般都定义为i - 1j - 1,目的是精简初始化的步骤)

2、DP数组初始化:首行与首列元素无意义,但为了递推公式将其初始化为0,其余元素随意

3、递推公式:如果nums1[i - 1] == nums2[j - 1],那么dp[i][j] = dp[i - 1][j - 1] + 1

· 基本------dp[i - 1][j - 1]:以nums1[i - 2]和nums2[j - 1]为结尾的两个字符串的最长重复子数组长度

· 新增------ +1:加上新的这对匹配元素

· 最后的递推公式:dp[i] = std::max(dp[i], dp[j] + 1)

4、遍历顺序:从上到下从左到右遍历,先遍历nums1或nums2都可以

cpp 复制代码
int findLength(vector<int>& nums1, vector<int>& nums2) {
	// dp[i][j]表示以nums1[i - 1]和nums2[j - 1]为结尾的两个字符串的最长重复子数组长度
	// 首行与首列元素无意义,为了递推公式将其初始化为0
	vector<vector<int>> dp(nums1.size() + 1, vector<int>(nums2.size() + 1, 0));

	int ans = 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]递推得到
				dp[i][j] = dp[i - 1][j - 1] + 1;
				ans = std::max(ans, dp[i][j]);
			}
		}
	}
	return ans;
}
相关推荐
登山人在路上24 分钟前
Nginx三种会话保持算法对比
算法·哈希算法·散列表
写代码的小球1 小时前
C++计算器(学生版)
c++·算法
AI科技星1 小时前
张祥前统一场论宇宙大统一方程的求导验证
服务器·人工智能·科技·线性代数·算法·生活
予枫的编程笔记1 小时前
Redis 核心数据结构深度解密:从基础命令到源码架构
java·数据结构·数据库·redis·缓存·架构
wadesir1 小时前
掌握Rust并发数据结构(从零开始构建线程安全的多线程应用)
数据结构·安全·rust
Fuly10242 小时前
大模型剪枝(Pruning)技术简介
算法·机器学习·剪枝
Xの哲學2 小时前
Linux网卡注册流程深度解析: 从硬件探测到网络栈
linux·服务器·网络·算法·边缘计算
bubiyoushang8882 小时前
二维地质模型的表面重力值和重力异常计算
算法
仙俊红2 小时前
LeetCode322零钱兑换
算法
颖风船2 小时前
锂电池SOC估计的一种算法(改进无迹卡尔曼滤波)
python·算法·信号处理