1562. 查找大小为 M 的最新分组(动态维护区间)

题目链接

讲解链接

题目描述
给出一个非负整数的数据流 a1,a2,…an,…a1,a2,…an,…,请将它们动态地维护成一系列不相交的区间。

思考题: 假设有非常多的区间合并操作,并且区间总数远小于所有数的个数时,该怎么做?

样例
给定数据流:1, 3, 7, 2, 6, ..., 动态得到的区间是:
[1, 1]
[1, 1], [3, 3]
[1, 1], [3, 3], [7, 7]
[1, 3], [7, 7]
[1, 3], [6, 7]

算法
(平衡树) addNum: O(logn), getIntervals: O(n)
我们用 map<int,int> L, R 来动态维护所有区间,L可以从区间右端点索引到左端点,R可以从区间左端点索引到右端点。
举例来说,假设有个区间是[x, y],则L[y] = x并且R[x] = y。

对于addNum(val)操作:

我们先判断val是否已经包含在某个区间中。具体来说,先用upper_bound(val)找出最后一个左端点小于等于val的区间,然后判断该区间的右端点是否大于等于val。
如果val不包含在任何区间中,则分别判断val-1和val+1是否存在:
如果都存在,则合并左右区间,合并方式:R[L[val - 1]] = R[val + 1],L[R[val + 1]] = L[val - 1],然后将R[val+1]和L[val-1]删除;
如果只有一边存在,则将val插入该区间中;
如果都不存在,则将val表示成一个单独的区间;
对于getIntervals()操作,直接输出所有区间即可。

时间复杂度分析:对于addNum操作,只涉及常数次平衡树的增删改查操作,所以时间复杂度是 O(logn)。对于getIntervals操作,需要将整棵平衡树遍历一遍,所以时间复杂度是 O(n)

 

/**
 * Definition for an interval.
 * struct Interval {
 *     int start;
 *     int end;
 *     Interval() : start(0), end(0) {}
 *     Interval(int s, int e) : start(s), end(e) {}
 * };
 */
class SummaryRanges {
public:
    /** Initialize your data structure here. */
    map<int,int>L, R;
    SummaryRanges() {

    }

    void addNum(int val) {
        if (R.size())
        {
            auto it = R.upper_bound(val);
            if (it != R.begin())
            {
                -- it;
                if (it->second >= val) return;
            }
        }
        int right = R.count(val + 1), left = L.count(val - 1);
        if (left && right)
        {
            R[L[val - 1]] = R[val + 1];
            L[R[val + 1]] = L[val - 1];
            R.erase(val + 1), L.erase(val - 1);
        }
        else if (right)
        {
            L[R[val + 1]] = val;
            R[val] = R[val + 1];
            R.erase(val + 1);
        }
        else if (left)
        {
            R[L[val - 1]] = val;
            L[val] = L[val - 1];
            L.erase(val - 1);
        }
        else
        {
            R[val] = val;
            L[val] = val;
        }
    }

    vector<Interval> getIntervals() {
        vector<Interval> res;
        for (auto &p : R) res.push_back(Interval(p.first, p.second));
        return res;
    }
};

/**
 * Your SummaryRanges object will be instantiated and called as such:
 * SummaryRanges obj = new SummaryRanges();
 * obj.addNum(val);
 * vector<Interval> param_2 = obj.getIntervals();
 */

作者:yxc
链接:https://www.acwing.com/solution/content/373/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

例二:

题目链接

视频链接

给你一个数组 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 <= 10^5
1 <= arr[i] <= n
arr 中的所有整数 互不相同
1 <= m <= arr.length

 

class Solution {
public:
    vector<int> l, r;
    int get(int x) {
        return r[x] - x + 1;
    }
    int findLatestStep(vector<int>& arr, int m) {
        int n = arr.size();
        l.resize(n + 2), r.resize(n + 2);
        int cnt = 0, ans = -1;
        for(int i=0;i<arr.size();i++){
            int x=arr[i];
            if(l[x-1]&&r[x+1]){
                if(get(l[x-1])==m) cnt--;
                if(get(x+1)==m) cnt--;
                r[l[x-1]]=r[x+1];
                l[r[x+1]]=l[x-1];
                if(get(l[x-1])==m) cnt++;
            }
            else if(l[x-1]){
                if(get(l[x-1])==m) cnt--;
                r[l[x-1]]=x,l[x]=l[x-1];
                if(get(l[x-1])==m) cnt++;
            }
            else if(r[x+1]){
                if(get(x+1)==m) cnt--;
                r[x]=r[x+1];
                l[r[x+1]]=x;
                if(get(x)==m) cnt++;
            }
            else{
                l[x]=r[x]=x;
                if(m==1) cnt++;
            }
            if(cnt) ans=i+1;
        }
        return ans;
    }
};

 

posted @ 2021-05-06 21:30  lipu123  阅读(79)  评论(0)    收藏  举报