C++二分算法:得到子序列的最少操作次数

这篇具有很好参考价值的文章主要介绍了C++二分算法:得到子序列的最少操作次数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文涉及的基础知识点

二分查找算法合集

题目

给你一个数组 target ,包含若干 互不相同 的整数,以及另一个整数数组 arr ,arr 可能 包含重复元素。
每一次操作中,你可以在 arr 的任意位置插入任一整数。比方说,如果 arr = [1,4,1,2] ,那么你可以在中间添加 3 得到 [1,4,3,1,2] 。你可以在数组最开始或最后面添加整数。
请你返回 最少 操作次数,使得 target 成为 arr 的一个子序列。
一个数组的 子序列 指的是删除原数组的某些元素(可能一个元素都不删除),同时不改变其余元素的相对顺序得到的数组。比方说,[2,7,4] 是 [4,2,3,7,2,1,4] 的子序列(加粗元素),但 [2,4,2] 不是子序列。
示例 1:
输入:target = [5,1,3], arr = [9,4,2,3,4]
输出:2
解释:你可以添加 5 和 1 ,使得 arr 变为 [5,9,4,1,2,3,4] ,target 为 arr 的子序列。
示例 2:
输入:target = [6,4,8,1,3,2], arr = [4,7,6,2,3,8,6,1]
输出:3
参数范围
1 <= target.length, arr.length <= 105
1 <= target[i], arr[i] <= 109
target 不包含任何重复元素。

分析

时间复杂度

O(nlogn),枚举arr的时间复杂度O(n),处理arr的每个元素都需要二分查找,时间复杂度O(n)。

最长公共子序列

本题转换一下就是:求 target的长度减两者的公共最长子序列的长度。

注意

target 中没有重复的元素,所以可以将nums[i]替换成它的索引。比如: target= {3,2,1},arr={2,3}。
替换成{0,1,2},{1,0}。arr存在,但target中不存在的元素,忽略掉,比如:设置为-1,处理的时候忽略掉。

大致步骤。

一,值变索引。

 mValueIndex[target[i]] = i;

二,依次枚举arr[i]。

for (const auto& n : arr)

vLenToEndIndex

vLenToEndIndex见的淘汰

vLenToEndIndex[i]如果有多个值,淘汰值大的。因为索引越小越容易组成新的子序列。

含义

vLenToEndIndex[i]为j,表示目前长度为i+1的子序列的末尾元素的值(同时也是target中的索引)是j。
构建以n结果的公共子序列的两者方法:

只有n一个元素的子序列
如果存在j<n,且以j结尾的公共序列 此系列+i

令n在 arr中的索引为i,则除掉被淘汰的公共子序列,以arr[0,i)结尾的公共子序列都在vLenToEndIndex中。vLenToEndIndex[j]小于n,说明vLenToEndIndex[j]在target的位置在n之前。也就是此子系列的结尾元素在target和arr中,都在n之前,故可以组成新的子序列。如果有多个vLenToEndIndex[j]符合条件取最大j。j+1就是新系列的长度。

vLenToEndIndex是严格递增

一,初始状态下,空向量符合严格递增。
二,如果vLenToEndIndex所有元素小于n,则n追加到最后,显然是严格递增。
三,it是第一个大于等于n的数。也就是a,it之前的数都小于n。b,由于vLenToEndIndex是严格递增,所有it后面的数大于it,而it>=n,故后面的元素>n。所以以下代码不会影响严格递增。

*it = n;

代码

核心代码

class Solution {
public:
int minOperations(vector& target, vector& arr) {
unordered_map<int, int> mValueIndex;
for (int i = 0; i < target.size(); i++)
{
mValueIndex[target[i]] = i;
}
for (auto& n : arr)
{
if (mValueIndex.count(n))
{
n = mValueIndex[n];
}
else
{
n = -1;
}
}
vector vLenToEndIndex;
for (const auto& n : arr)
{
if (-1 == n)
{
continue;
}
auto it = std::lower_bound(vLenToEndIndex.begin(), vLenToEndIndex.end(), n);
if (vLenToEndIndex.end() == it)
{
vLenToEndIndex.emplace_back(n);
}
else
{
if (n < *it)
{
*it = n;
}
}
}
return target.size() - vLenToEndIndex.size();
}
};

优化后的代码

直接将符合的arr[i]复制到新数组中。

class Solution {
public:
	int minOperations(vector<int>& target, const vector<int>& arr) {
		unordered_map<int, int> mValueIndex;
		for (int i = 0; i < target.size(); i++)
		{
			mValueIndex[target[i]] = i;
		}
		vector<int> vNew;
		for (auto& n : arr)
		{
			if (mValueIndex.count(n))
			{
				vNew.emplace_back(mValueIndex[n]);
			}
		}
		vector<int> vLenToEndIndex;
		for (const auto& n : vNew)
		{
			auto it = std::lower_bound(vLenToEndIndex.begin(), vLenToEndIndex.end(), n);
			if (vLenToEndIndex.end() == it)
			{
				vLenToEndIndex.emplace_back(n);
			}
			else
			{
				if (n < *it)
				{
					*it = n;
				}
			}
		}
		return target.size() - vLenToEndIndex.size();
	}
};

测试用例

template
void Assert(const T& t1, const T& t2)
{
assert(t1 == t2);
}

template
void Assert(const vector& v1, const vector& v2)
{
if (v1.size() != v2.size())
{
assert(false);
return;
}
for (int i = 0; i < v1.size(); i++)
{
Assert(v1[i], v2[i]);
}
}

int main()
{
vector target, arr;

int res;
{
	Solution slu;
	target = { 5, 1, 3 }, arr = { 9, 4, 2, 3, 4 };
	res = slu.minOperations(target, arr);
	Assert(2, res);
}
{
	Solution slu;
	target = { 6,4,8,1,3,2 }, arr = { 4,7,6,2,3,8,6,1 };
	res = slu.minOperations(target, arr);
	Assert(3, res);
}

//CConsole::Out(res);

}

2023年3月旧版

class Solution {
public:
int minOperations(vector& target, vector& arr) {
std::unordered_map<int, int> mValueToIndex;
for (int i = 0; i < target.size(); i++)
{
mValueToIndex[target[i]] = i+1;
}
for (const auto& a : arr)
{
if (mValueToIndex.count(a))
{
m_arr.push_back(mValueToIndex[a]);
}
}
Do();
return target.size() - m_iMaxPublicNum;
}
void Do()
{
std::map<int, int> mValueNum;
for (const auto& a : m_arr)
{
auto it = mValueNum.lower_bound(a);
int iNum = 1;
if (mValueNum.begin() != it)
{
auto tmp = it;
iNum = (–tmp)->second + 1;
}
if (mValueNum.end() != it)
{
if (iNum >= it->second)
{
mValueNum.erase(it);
}
}
m_iMaxPublicNum = max(m_iMaxPublicNum, iNum);
mValueNum[a] = iNum;
}
}
vector m_arr;
int m_iMaxPublicNum=0;//最大公共系列
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653文章来源地址https://www.toymoban.com/news/detail-754483.html

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

到了这里,关于C++二分算法:得到子序列的最少操作次数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【算法题】2602. 使数组元素全部相等的最少操作次数

    给你一个正整数数组 nums 。 同时给你一个长度为 m 的整数数组 queries 。第 i 个查询中,你需要将 nums 中所有元素变成 queries[i] 。你可以执行以下操作 任意 次: 将数组里一个元素 增大 或者 减小 1 。 请你返回一个长度为 m 的数组 answer ,其中 answer[i]是将 nums 中所有元素变成

    2023年04月24日
    浏览(34)
  • C++二分算法(二分查找&二分答案)细节详解

     二分算法可以分为 二分查找 和 二分答案 。 以在一个 升序数组 中查找一个数为例。它每次考察数组当前部分的 中间元素 ,如果中间元素刚好是要找的,就结束搜索过程;如果中间元素小于所查找的值,那么左侧的只会更小,不会有所查找的元素,只需到右侧查找;如果

    2024年02月08日
    浏览(52)
  • 【LeetCode 算法】Minimum Operations to Halve Array Sum 将数组和减半的最少操作次数-Greedy

    给你一个正整数数组 nums 。每一次操作中,你可以从 nums 中选择 任意 一个数并将它 减小 到 恰好 一半 。(注意,在后续操作中你可以对减半过的数继续执行操作) 请你返回将 nums 数组和 至少 减少一半 的 最少 操作数。 1 = n u m s . l e n g t h = 1 0 5 1 = n u m s [ i ] = 1 0 7 1 = num

    2024年02月15日
    浏览(44)
  • 二分查找算法讲解及其C++代码实现

    二分查找算法是一种常用的查找算法,也被称为折半查找。它可以在有序的数组或列表中快速查找需要的元素。 算法描述: 首先确定数组的中间位置mid=(left+right)/2; 然后将要查找的值key与中间位置的值进行比较; 如果key等于中间位置的值,则查找成功,返回mid; 如果key小

    2024年02月01日
    浏览(43)
  • C++二分查找算法:132 模式枚举3

    本篇是视频课程的讲义,可以看直接查看视频。也可以下载源码,包括空源码。 二分查找算法合集 包括题目及代码 C++二分查找算法:132 模式解法一枚举3 C++二分查找算法:132 模式解法二枚举2 代码最简洁 C++二分查找算法:132 模式解法三枚举1 性能最佳 C++单调向量算法:1

    2024年02月04日
    浏览(40)
  • 【动态规划】【二分查找】C++算法 466 统计重复个数

    视频算法专题 动态规划汇总 二分查找 定义 str = [s, n] 表示 str 由 n 个字符串 s 连接构成。 例如,str == [“abc”, 3] ==“abcabcabc” 。 如果可以从 s2 中删除某些字符使其变为 s1,则称字符串 s1 可以从字符串 s2 获得。 例如,根据定义,s1 = “abc” 可以从 s2 = “abdbec” 获得,仅需

    2024年01月23日
    浏览(47)
  • C++二分查找算法:132 模式解法二枚举2

    二分查找算法合集 包括题目及代码 C++二分查找算法:132 模式解法一枚举3 C++二分查找算法:132 模式解法二枚举2 代码简洁 C++二分查找算法:132 模式解法三枚举1 性能最佳 C++单调向量算法:132 模式解法三枚举1 代码更简洁 C++二分查找算法:132模式枚举3简洁版 代码简洁,性能

    2024年02月05日
    浏览(43)
  • 【动态规划】【C++算法】801. 使序列递增的最小交换次数

    【动态规划】【广度优先搜索】【状态压缩】847 访问所有节点的最短路径 动态规划汇总 数组 我们有两个长度相等且不为空的整型数组 nums1 和 nums2 。在一次操作中,我们可以交换 nums1[i] 和 nums2[i]的元素。 例如,如果 nums1 = [1,2,3,8] , nums2 =[5,6,7,4] ,你可以交换 i = 3 处的元素

    2024年01月22日
    浏览(42)
  • 将数组和减半的最少操作次数(力扣)

    给你一个正整数数组 nums 。每一次操作中,你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半。(注意,在后续操作中你可以对减半过的数继续执行操作) 请你返回将 nums 数组和 至少 减少一半的 最少 操作数。 示例 1: 示例 2: 每次操作,会将数组中的一个数减半。

    2024年02月16日
    浏览(36)
  • C++二分查找算法:有序矩阵中的第 k 个最小数组和

    二分查找算法合集 C++二分查找算法:查找和最小的 K 对数字 十分接近m恒等于2 给你一个 m * n 的矩阵 mat,以及一个整数 k ,矩阵中的每一行都以非递减的顺序排列。 你可以从每一行中选出 1 个元素形成一个数组。返回所有可能数组中的第 k 个 最小 数组和。 示例 1: 输入:

    2024年02月05日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包