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

300.最长递增子序列

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

1、DP数组定义 :dpi表示以numsi为最后一个元素的最长递增子序列长度

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

3、递推公式 :++与 i 前所有元素进行对比++,如果numsi > numsj,那么更新dpi

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

· 新增------ +1:算上numsi,多了一个递增元素

· 最后的递推公式:dpi取较大值:dpi = std::max(dpi, dpj + 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数组定义 :两个维度表示两个数组的索引,dpij表示以nums1i - 1 和**nums2j - 1**为结尾的两个字符串的最长重复子数组长度

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

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

3、递推公式:如果nums1i - 1 == nums2j - 1,那么dpij = dpi - 1j - 1 + 1

· 基本------dpi - 1j - 1:以nums1i - 2和nums2j - 1为结尾的两个字符串的最长重复子数组长度

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

· 最后的递推公式:dpi = std::max(dpi, dpj + 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;
}
相关推荐
江畔柳前堤6 小时前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
Doraemomo6 小时前
数据结构-环形链表
java·数据结构·链表
Forever Nore7 小时前
LeetCode 4 寻找两个正序数组的中位数 - 二分
算法·leetcode
罗西的思考9 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX9 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面10 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号10 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽10 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI11 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
MC皮蛋侠客12 小时前
Redis 系列(一):全景与最小闭环——从 `SET` 命令到内存数据结构
数据结构·数据库·redis