zoukankan      html  css  js  c++  java
  • 双周赛 57 题解

    四道题都不难,中间两题卡了我比较长时间,幸亏最后一题是个单调栈一眼题,不然就要掉大分了

    知识点:优先队列,差分数组,单调栈

    检查是否所有字符出现次数相同

    给定一个字符串 (s),如果 (s) 中所有字符串出现的次数一样,那么我们称之为好字符串,请返回 (s) 是否是好字符串

    题解

    把每一个字符出现的次数用桶维护,扔到集合里面去,利用集合的去重功能,判断集合的 size 是否为 (1)

    class Solution {
    public:
        bool areOccurrencesEqual(string s) {
            vector<int> a(26);
            int n = s.length();
            set<int> st;
            for (int i = 0; i < n; ++i) a[s[i] - 'a']++;
            for (int i = 0; i < 26; ++i)
                if (a[i]) st.insert(a[i]);
            return st.size() == 1;
        }
    };
    

    最小未被占据椅子的编号

    (n) 个人在办聚会,有无限个椅子,每个人有到达时间和离开时间,每个人到达聚会的时候会选择空闲的最小编号的椅子

    现在给定 (n) 个人的到达时间和离开时间,计算出第 (t) 个人坐的椅子的编号

    题解

    用一个优先队列存储每个椅子的空闲时间和编号,用一个集合存储 (n) 个椅子的编号

    遍历 (n) 个人,根据到达时间和离开时间处理空闲和忙碌的椅子,更新优先队列和集合即可

    // cpp
    struct node {
        int idx, ed;
        node() {}
        node(int _idx, int _ed):
            idx(_idx), ed(_ed) {}
    };
    bool operator< (const node& x, const node& y) {
        if (x.ed == y.ed) return x.idx < y.idx;
        return x.ed > y.ed;
    }
    
    bool cmp(vector<int> &x, vector<int> &y) {
        if (x[0] == y[0]) return x[1] < y[1];
        return x[0] < y[0];
    }
    class Solution {
    public:
        int smallestChair(vector<vector<int>>& t, int target) {
            vector<int> vec;
            int n = t.size();
            for (int i = 0; i < n; ++i) vec.push_back(i), t[i].push_back(i);
            set<int> st(vec.begin(), vec.end());
            priority_queue<node> pq;
            sort(t.begin(), t.end(), cmp);
            int res = 0;
            for (int i = 0; i < n; ++i) {
                while (!pq.empty()) {
                    node temp = pq.top();
                    if (temp.ed <= t[i][0]) pq.pop(), st.insert(temp.idx);
                    else break;
                }
                res = *st.begin();
                pq.push(node(res, t[i][1]));
                st.erase(st.begin());
                if (t[i][2] == target) break;
            }
            return res;
        }
    };
    

    描述绘画结果

    给定 (n) 个左闭右开区间 ([l, r)),并给这个区间上每一个值加上 (w)

    返回操作后的数轴,要求将相同值写成左闭右开区间的形式,值为 (0) 的区间跳过

    举例来讲,如果 (1, 2, 3) 的值都是 (12),并且 (4) 的值为 (13),那我们可以把其合并成 ([1, 4), [4, 5))

    注意,对于值相同的区间,如果达到这个和的方式不一样,不能写成一个区间

    例如 ([1, 4), [4, 7)) 均为 (12),但前者为 (5 + 7),后者为 (3 + 9),此时我们不能将其合并

    题解

    原题题意比较冗余,也很难抽象题意描述,但是考点很简单:差分数组

    差分数组说的是:做 (n) 次区间加法操作,可以用离线算法在 (O(n)) 时间得到更新后的区间

    具体来讲,如果要给区间 ([l, r)) 统一加上 (w),只需要在 (l) 处加上 (w),在 (r) 处减去 (w),如此进行 (n) 次,最后对原数组进行前缀和,便可以得到更新后的数组,这里的「原数组」被称为差分数组

    我们对更新后的数组进行划分,将相同的值归纳为一个区间即可

    此题在差分数组的基础上多了一个要求:对于值相同的区间,如果达到这个和的方式不一样,不能写成一个区间

    这个也比较好处理,利用贪心的思想,继续构造一个差分数组,区间加「区间右端点」的值即可,聪明的你能想明白贪心的正确性吗?

    时间复杂度 (O(n))

    // cpp
    typedef long long LL;
    class Solution {
    public:
        vector<vector<long long>> splitPainting(vector<vector<int>>& seg) {
            int n = seg.size();
            int maxx = 0;
            for (int i = 0; i < n; ++i) {
                maxx = max(maxx, seg[i][1]);
                seg[i].push_back(i + 1);
            }
            vector<LL> vec1(maxx + 7);
            vector<LL> vec2(maxx + 7);
            for (int i = 0; i < n; ++i) {
                int a = seg[i][0], b = seg[i][1], c = seg[i][2], d = seg[i][3];
                vec1[a] += c, vec1[b] -= c;
                vec2[a] += d, vec2[b] -= d;
            }
            for (int i = 1; i <= maxx; ++i) {
                vec1[i] += vec1[i - 1];
                vec2[i] += vec2[i - 1];
            }
            vector<vector<LL>> ans;
            int a = 1, b = 1;
            for (int i = 1; i <= maxx; ++i) {
                if (vec1[i] != vec1[a] || vec1[i] == vec1[a] && vec2[i] != vec2[a]) {
                    if (!vec1[i - 1]) {a = b = i; continue;}
                    else {
                        ans.push_back(vector<LL>{a, i, vec1[i - 1]});
                        a = b = i;
                    }
                }
            }
            return ans;
        }
    };
    

    从队列中可以看到的人数

    给定 (n) 个人及其身高 (h_{i})

    对于两个人 (i, j),其中 (i)(j) 左边,如果 (i, j) 中间的每一个人都没有他们高,那么 (i) 可以看到右边的 (j)

    计算出每个人可以看到的右边的人数

    数据规定

    (1leq nleq 10^5)

    题解

    对于当前的 (i) 而言,我们可以计算出他左边第一个比他高的人 (L),以及右边第一个比他高的人 (R)

    如果 (L) 存在,那么 (L) 可以看到 (i)

    如果 (R) 存在,那么 (i) 可以看到 (R)

    使用单调栈预处理即可,时间复杂度 (O(n))

    // cpp
    class Solution {
    public:
        vector<int> canSeePersonsCount(vector<int>& h) {
            int n = h.size();
            vector<int> rmax(n + 7);
            vector<int> lmax(n + 7);
            vector<int> ans(n + 7);
            vector<int> a(n + 7);
            for (int i = 1; i <= n; ++i) a[i] = h[i - 1];
            stack<int> mono1, mono2; // 递增
            for (int i = 1; i <= n; ++i) {
                while (!mono1.empty() && a[mono1.top()] < a[i]) {
                    rmax[mono1.top()] = i;
                    mono1.pop();
                }
                mono1.push(i);
            }
            for (int i = n; i >= 1; --i) {
                while (!mono2.empty() && a[mono2.top()] < a[i]) {
                    lmax[mono2.top()] = i;
                    mono2.pop();
                }
                mono2.push(i);
            }
            for (int i = 1; i <= n; ++i) {
                int L = lmax[i], R = rmax[i];
                if (L >= 1 && L <= n) ans[L]++;
                if (R >= 1 && R <= n) ans[i]++;
            }
            return {ans.begin() + 1, ans.begin() + 1 + n};
        }
    };
    
  • 相关阅读:
    LeetCode --- 字符串系列 --- 解码字母到整数映射
    LeetCode --- 字符串系列 --- 上升下降字符串
    LeetCode --- 字符串系列 --- 机器人能否返回原点
    集合 Set
    LeetCode --- 字符串系列 --- 唯一摩尔斯密码词
    LeetCode --- 字符串系列 --- 转换成小写字母
    LeetCode --- 字符串系列 --- 分割平衡字符串
    LeetCode --- 字符串系列 --- IP 地址无效化
    LeetCode --- 字符串系列 --- 左旋转字符串
    Revit二次开发八 事务标签值
  • 原文地址:https://www.cnblogs.com/ChenyangXu/p/15059231.html
Copyright © 2011-2022 走看看