算法初探LeetCode-查找大小为M的最新分组

165 阅读2分钟

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

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

在从 1 到 n 的每个步骤 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<=1051 <= n <= 10^5
  • 1 <= arr[i] <= n
  • arr 中的所有整数 互不相同
  • 1 <= m <= arr.length

思路分析

使用并查集,每遍历一个元素,将元素并入集合,合并符合条件的集合。并判断集合长度是否满足M,满足vaild+1,不满足分两种情况,第一种原先满足条件的集合被合并,此时需要vaild-1;第二种原先满足条件的集合未被合并,不需要进行任何操作。每次循环最后判断vaild是否大于0,大于更新last,小于或者等于不进行任何操作。

维护三个数组, nums[i]:表示以i为根节点的集合中元素的个数,初始值都为1,每一次集合合并都需要更新该数组 vis[i]:i元素是否被遍历过 set[i]:集合

算法代码

public int findLatestStep(int[] arr, int m) {
    if (arr == null || arr.length == 0) throw new IllegalArgumentException("invalid param");

    int n = arr.length;
    if (m > n) return -1;
    if (m == n) return n;

    // 转化为时间数组
    int[] times = new int[n];
    int time = 1;
    for (int num: arr) times[num - 1] = time++;

    // 问题转化为窗口m中最大值与左右边界的比较问题
    int[] left = new int[n], right = new int[n];
    for (int i = 0; i < n; i++) {
        if (i % m == 0) left[i] = times[i];
        else left[i] = Math.max(left[i - 1], times[i]);

        int j = n - i - 1;
        if (j == n - 1 || (j + 1) % m == 0) right[j] = times[j];
        else right[j] = Math.max(right[j + 1], times[j]);
    }

    int res = 0;
    // 遍历窗口
    for (int i = 0; i <= n - m; i++) {
        // 窗口最大值
        int max = Math.max(left[i + m - 1], right[i]);
        // 比较左右邻居
        if (i == 0 && times[m] > max) res = times[m];
        else if (i == n - m && times[n - m - 1] > max) res = Math.max(res, times[n - m - 1]);
        else if (i > 0 && i < n - m && times[i - 1] > max && times[i + m] > max) res = Math.max(res, Math.min(times[i - 1], times[i + m]));
    }

    return res - 1;
}

结果详情

Snipaste_2023-06-09_23-05-32.png

算法复杂度

  • 空间复杂度:O(1)O(1)
  • 时间复杂度:O(n)O(n)

掘金(JUEJIN)一起进步,一起成长!