leetcode1562. 查找大小为 M 的最新分组(Python3、c++)

这是一篇关于使用模拟维护区间的方法解决LeetCode1562题目的文章。通过维护左右边界left和right数组,动态更新连通区间长度,寻找最后一个长度为M的连续1区间。文章提供了Python3和C++两种实现代码。
摘要由CSDN通过智能技术生成

leetcode1562. 查找大小为 M 的最新分组

给你一个数组 arr ,该数组表示一个从 1n 的数字排列。有一个长度为 n 的二进制字符串,该字符串上的所有位最初都设置为 0

在从 1n 的每个步骤 i 中(假设二进制字符串和 arr 都是从 1 开始索引的情况下),二进制字符串上位于位置 arr[i] 的位将会设为 1

给你一个整数 m ,请你找出二进制字符串上存在长度为 m 的一组 1 的最后步骤。一组 1 是一个连续的、由 1 组成的子串,且左右两边不再有可以延伸的 1

返回存在长度 恰好m一组 1 的最后步骤。如果不存在这样的步骤,请返回 -1

示例 1:

输入:arr = [3,5,1,2,4], m = 1
输出:4
解释:
步骤 1:"00100",由 1 构成的组:["1"]
步骤 2:"00101",由 1 构成的组:["1", "1"]
步骤 3:"10101",由 1 构成的组:["1", "1", "1"]
步骤 4:"11101",由 1 构成的组:["111", "1"]
步骤 5:"11111",由 1 构成的组:["11111"]
存在长度为 1 的一组 1 的最后步骤是步骤 4 。

示例 2:

输入:arr = [3,1,5,4,2], m = 2
输出:-1
解释:
步骤 1:"00100",由 1 构成的组:["1"]
步骤 2:"10100",由 1 构成的组:["1", "1"]
步骤 3:"10101",由 1 构成的组:["1", "1", "1"]
步骤 4:"10111",由 1 构成的组:["1", "111"]
步骤 5:"11111",由 1 构成的组:["11111"]
不管是哪一步骤都无法形成长度为 2 的一组 1 。

示例 3:

输入:arr = [1], m = 1
输出:1

示例 4:

输入:arr = [2,1], m = 2
输出:2

提示:

  • n == arr.length
  • 1 <= n <= 10^5
  • 1 <= arr[i] <= n
  • arr 中的所有整数 互不相同
  • 1 <= m <= arr.length

方法:模拟维护区间

思路:

题意是按照arr的顺序不断将对应的0变成1,我们关注的是连续1的区间的长度。因此可以使用并查集来解决,相邻的两个点都是1,那么这两个点就可以合并,同时修改并查集定义,维护每个连通区间的长度,找到最后一次有长度为m的连通区间的步骤编号即可。

由于本题这些点是一位的,连通分量一定在一维上连续,因此我们可以简化,不使用并查集。我们维护两个数组left、right。

  • left[i]表示以结点i为连通分量右端点,它对应的左端点
  • right[i]表示以结点i为连通分量左端点,它对应的右端点

我们这种结构类似于双向链表,即对一个连通分量(即全是1的连通区间),左端点的right为右端点,右端点的left为左端点。因左端点的left无意义(因为左端点的左边肯定为0,因此以它为右端点的连通区间就是它自己,因此它的left设置为自己),同理右端点的right也设置为自己。

两个数组初始值都为0,长度应该为n+2(在下面介绍)。

这种情况下,我们如何计算连通区间的长度呢,很简单,我们输入连通区间的左端点坐标x,那么长度即为right[x]-x+1。我们用一个函数getlength来表示这个式子。

介绍完如何表示连通区间之后,我们介绍本题的总体思路,使用res来保存答案,初始为-1。使用变量count来保存当前长度为m的连通分量个数,刚开始肯定是0。

  • 开始按照arr顺序进行遍历,根据arr[i]的不同,即将0变为1的位置不同,更新left和right变量(后面详细解释)。
  • 对涉及到的,更新的连通区间进行求长度,如果原来是m,那么count–,如果新变成的区间是m,那么count++。
  • 更新结束后,如果此时count>0,更新res为i+1(这是因为i从0开始,而答案返回从第1步开始,因此要+1),即该步结束后,存在长度为m的连通区间。
  • 完成arr的遍历,返回res即可。如果前面遍历过程中,没有一次出现m,那么res为初始值-1;如果有,那么res为最后一次有长度为m的区间的步骤序号。

下面我们来详细讲解区间更新的步骤,可以知道,假设x=arr[i],那么更新的时候,x的左右可能出现四种情况:

  • 第一种是x的左右两边都是1,即左右两边都有连通区间了,那么最后要将这两个区间和x合并成一个区间。
  • 第二种是左边为0,右边是1,那么只有右边有连通区间,那么更新之后需要将右边区间扩大一位
  • 第三种是右边为0,左边是1,那么只有左边有连通区间,更新之后将左边区间扩大一位
  • 最后一种是左右都为0,那么就只有自己,更新之后生成长度为1的新连通区间。

上面一共四种情况我们需要考虑,首先需要说明的是,我们不需要使用一个数组来维护当前1-n的排列如何,因为如果某个位置已经是1了,根据我们上面的介绍,它已经存在于一个连通区间中,因此它的left和right肯定是不为0的,因此我们可以直接用left和right来判断。

下面说明如何判断x左边和右边是不是1,首先我们知道,对于x=arr[i],x点此时肯定是0,

  • 那么如果x-1是1,那么它肯定是某个区间的右端点,因此我们判断left[x-1]是否不为0,如果不为0,那么即存在左区间。且将left[x-1]送入getlength函数即可得到左区间长度。
  • 对于有区间,如果x+1是1,那么它肯定是某个区间的左端点,因此我们判断right[x+1]是否不为0,如果不为0,那么即存在右区间,且将x+1送入getlength即可得到右区间长度。
  • 由上面可以看出,我们考虑left[x-1]和right[x+1],x的取值为1-n,下标的取值为0-n+1,这就是为什么我们的两个数组长度应该为n+2。

下面我们考虑四种情况如何更新left和right。

第一种情况的图解如下图所示(其中有一些没用的左右连线就忽略了,因为我们只关心left[x-1]和right[x+1]):

**我们需要将左区间的左端点的right变为右区间的右端点,右区间的右端点的left变为左区间的左端点,中间点的这些不需要考虑,因为中间都是1了,继续遍历arr的过程中,也不会遍历到这些点了。**因此代码为:

				right[left[x-1]] = right[x+1]
                left[right[x+1]] = left[x-1]

然后判断新的区间长度是不是m,更新count。

同理,对于第二种和第三种情况,我们只需要考虑其中一边的更新,因为另一侧的端点为x,直接给出更新的代码:

			# 只有左边有区间
                # 将左边区间向右扩展一位,更新left和right
                right[
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值