Medium
题目描述
给你一个数组 arr,该数组表示从 1 到 n 的数字的排列。
你有一个长度为 n 的二进制字符串,该字符串上的所有位最初都设置为 0。在从 1 到 n 的每个步骤 i 中(假设二进制字符串和 arr 都是从 1 开始索引的情况下),二进制字符串上位置 arr[i] 的位设置为 1。
你还被给定一个整数 m。请你找出二进制字符串上存在长度为 m 的一组 1 的最后步骤。一组 1 是一个连续的、由若干个 1 组成的子字符串,且不能向任一方向扩展。
返回存在长度恰好为 m 的一组 1 的最后步骤。如果不存在这样的组,请返回 -1。
示例 1:
输入:arr = [3,5,1,2,4], m = 1
输出:4
解释:
步骤 1:"00100",分组:["1"]
步骤 2:"00101",分组:["1", "1"]
步骤 3:"10101",分组:["1", "1", "1"]
步骤 4:"11101",分组:["111", "1"]
步骤 5:"11111",分组:["11111"]
存在长度为 1 的分组的最后步骤是步骤 4。
示例 2:
输入:arr = [3,1,5,4,2], m = 2
输出:-1
解释:
步骤 1:"00100",分组:["1"]
步骤 2:"10100",分组:["1", "1"]
步骤 3:"10101",分组:["1", "1", "1"]
步骤 4:"10111",分组:["1", "111"]
步骤 5:"11111",分组:["11111"]
在任何步骤中都不存在长度为 2 的分组。
约束条件:
n == arr.length1 <= m <= n <= 10^51 <= arr[i] <= narr中的所有整数都不相同。
解题思路
这道题可以通过两种思路来解决:
方法一:正向模拟 从前往后遍历数组,维护连续的1的分组信息。每次添加一个1时,需要检查是否与相邻的分组合并。使用哈希表记录每个分组长度的数量,当长度为m的分组数量大于0时更新答案。
方法二:反向思考(推荐) 从后往前遍历数组,相当于从全1的状态开始,逐步移除某些位置的1。这样问题转化为:移除一个1后,原来的一个长分组会分裂成两个分组。使用并查集或区间映射来维护分组信息。
具体实现中,我们可以使用哈希表记录每个长度分组的数量。对于反向思考的方法:
- 从最后一步开始,逐步"撤销"设置1的操作
- 每次撤销时,一个长分组会分裂成两个短分组
- 维护长度为m的分组数量,当数量>0时更新答案
这种方法的优势是避免了复杂的区间合并逻辑,而是通过分裂来简化处理过程。
代码实现
class Solution {
public:
int findLatestStep(vector<int>& arr, int m) {
int n = arr.size();
if (m == n) return n;
vector<int> lengths(n + 2, 0);
vector<int> count(n + 1, 0);
count[n] = 1;
for (int i = n - 1; i >= 0; i--) {
int pos = arr[i];
int left = lengths[pos - 1];
int right = lengths[pos + 1];
count[left + right + 1]--;
if (count[m] > 0) return i;
count[left]++;
count[right]++;
lengths[pos - left] = left;
lengths[pos + right] = right;
}
return -1;
}
};
class Solution:
def findLatestStep(self, arr: List[int], m: int) -> int:
n = len(arr)
if m == n:
return n
lengths = [0] * (n + 2)
count = [0] * (n + 1)
count[n] = 1
for i in range(n - 1, -1, -1):
pos = arr[i]
left = lengths[pos - 1]
right = lengths[pos + 1]
count[left + right + 1] -= 1
if count[m] > 0:
return i
count[left] += 1
count[right] += 1
lengths[pos - left] = left
lengths[pos + right] = right
return -1
public class Solution {
public int FindLatestStep(int[] arr, int m) {
int n = arr.Length;
if (m == n) return n;
int[] lengths = new int[n + 2];
int[] count = new int[n + 1];
count[n] = 1;
for (int i = n - 1; i >= 0; i--) {
int pos = arr[i];
int left = lengths[pos - 1];
int right = lengths[pos + 1];
count[left + right + 1]--;
if (count[m] > 0) return i;
count[left]++;
count[right]++;
lengths[pos - left] = left;
lengths[pos + right] = right;
}
return -1;
}
}
/**
* @param {number[]} arr
* @param {number} m
* @return {number}
*/
var findLatestStep = function(arr, m) {
const n = arr.length;
const lengths = new Array(n + 2).fill(0);
const count = new Array(n + 1).fill(0);
let result = -1;
for (let i = 0; i < n; i++) {
const pos = arr[i];
const leftLen = lengths[pos - 1];
const rightLen = lengths[pos + 1];
const newLen = leftLen + rightLen + 1;
lengths[pos - leftLen] = newLen;
lengths[pos + rightLen] = newLen;
count[leftLen]--;
count[rightLen]--;
count[newLen]++;
if (count[m] > 0) {
result = i + 1;
}
}
return result;
};
复杂度分析
| 复杂度类型 | 大小 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(n) |