• 双周赛 51,单周赛 239 题解


    本场比赛涵盖很多知识点,具体为 dfs,构造,双指针,set,单调队列,排列距离,逆序数

    双周赛 (51)

    将所有数字用字符替换

    题意

    给定一个下标从 (0) 开始的字符串 s,其中偶数下标处为小写字母 alpha,奇数下标处为数字 num,现在将所有奇数下标的数字替换成小写字母 alpha + num,保证 alpha + num <= 'z'

    例如 a1b2,将被替换成 abbda1c1e1 将被替换成 abcdef

    题解

    签到,按照题意模拟

    class Solution {
    public:
        string replaceDigits(string s) {
            for (int i = 0; i < s.length(); ++i)
                if (i % 2 == 1) s[i] = s[i - 1] + s[i] - '0';
            return s;
        }
    };
    

    座位预约管理系统

    题意

    设计一个管理 (n) 个座位的系统,座位从 (1) 编号到 (n)

    请你实现 SeatManager 类

    • SeatManager(int n) 初始化一个 SeatManager 对象,它管理从 1 到 n 编号的 n 个座位。所有座位初始都是可预约的。
    • int reserve() 返回可以预约座位的最小编号,并且此座位变为不可预约。
    • void unreserve(int seatNumber) 将给定编号  seatNumber 对应的座位变成可以预约。

    (1leq nleq 100,000)

    题解

    维护一个 set,初始化时将 (1)(n) 全部扔到 set 中,预约时返回 set.begin(),同时删除 set.begin(),取消预约时 set.insert(seatnumver)

    class SeatManager {
    public:
        set<int> st;
        SeatManager(int n) {
            for (int i = 1; i <= n; ++i)
                st.insert(i);
        }
        
        int reserve() {
            int temp = *st.begin();
            st.erase(st.begin());
            return temp;
        }
        
        void unreserve(int seatNumber) {
            st.insert(seatNumber);
        }
    };
    

    减小和重新排列数组后的最大元素

    题意

    给一个正整数数组 arr,可以执行一些操作(也可以不进行任何操作),使得数组满足以下条件

    • arr 中第一个元素必须为 1
    • 任意相邻两个元素的差的绝对值小于等于 1 ,也就是说,对于任意的 1 <= i < arr.length(数组下标从 0 开始),都满足 abs(arr[i] - arr[i - 1]) <= 1abs(x) 为 x 的绝对值。

    你可以执行以下 2 种操作任意次数

    • 减小 arr 中任意元素的值,使其变为一个更小的正整数
    • 重新排列 arr 中的元素,你可以以任意顺序重新排列。

    请你返回执行以上操作后,在满足前文所述的条件下,arr 中可能的最大值。

    (1leq arr.lengthleq 100,000)

    题解

    既然可以重排列,那么我们可以对数组排序

    我们可以把任意元素变小,那么只需要把数组头设为 1,剩下的元素取 min(arr[i - 1] + 1, arr[i]) 即可

    class Solution {
    public:
        int maximumElementAfterDecrementingAndRearranging(vector<int>& arr) {
            sort(arr.begin(), arr.end());
            if (arr[0] != 1) arr[0] = 1;
            for (int i = 1; i < arr.size(); ++i)
                arr[i] = min(arr[i], arr[i - 1] + 1);
            int ans = *max_element(arr.begin(), arr.end());
            return ans;
        }
    };
    

    最近的房间

    题意

    给定 (n) 个房间,每个房间的结构为二元组 (roomID, size),分别代表房间 ID 和房间面积,保证房间 ID 两两不同

    (k) 个查询,每个查询为二元组 (preferred, minSize)

    现在要求满足 size >= minSIze 同时 abs(roomID - preferred) 最小的房间 ID

    如果 abs(roomID - preferred) 相等,那么取小的 roomID,例如 preferred = 4, roomID = 3, 5 的两个房间同时满足题意,选择 roomID = 3,如果没有这样的房间,答案为 -1

    (1leq nleq 100,000, 1leq kleq 10,000)

    题解

    如果不考虑面积,只考虑计算最小的 roomID,使得 abs(roomID - preferred) 最小,我们可以维护有序数组 roomID,并二分查找 preferred 前后的两个位置,比较得到最小值,这个可以用 set 维护,lower_bound 计算

    考虑面积,若将房间和询问按照面积排序,我们发现倒序枚举询问有奇效。倒序枚举询问时,需求的 minSize 在递减,那么满足 size >= minSize 要求的房间就更多

    可以动态维护一个 set,倒序枚举询问,利用双指针不断扩充 set,同时在 set 中二分查找,计算答案

    每个房间二元组都只扫描一次,时间复杂度 (O(nlogn + sumlimits_{i = 1}^{n}{log_{2}i}) = O(nlogn))

    #define pb push_back
    class Solution {
    public:
        static bool cmp(vector<int> &x, vector<int> &y)
        {
            if (x[1] == y[1]) return x[0] < y[0];
            return x[1] < y[1];
        }
        vector<int> closestRoom(vector<vector<int>>& rooms, vector<vector<int>>& queries) {
            int n = rooms.size();
            int k = queries.size();
            for (int i = 0; i < n; ++i) rooms[i].pb(i);
            for (int i = 0; i < k; ++i) queries[i].pb(i);
            vector<int> ans(k);
            sort(rooms.begin(), rooms.end(), cmp);
            sort(queries.begin(), queries.end(), cmp);
            set<int> st;
            for (int i = k - 1, j = n - 1; i >= 0; --i) {
                while (j >= 0 && rooms[j][1] >= queries[i][1]) st.insert(rooms[j--][0]);
                int res = queries[i][2];
                if (st.empty()) ans[res] = -1;
                else {
                    auto it = st.lower_bound(queries[i][0]);
                    if (it == st.end()) ans[res] = *(--it);
                    else if (it == st.begin()) ans[res] = *it;
                    else {
                        int x = *it, y = *(--it);
                        int dis1 = abs(x - queries[i][0]), dis2 = abs(y - queries[i][0]);
                        if (dis1 < dis2) ans[res] = x;
                        else ans[res] = y;
                    }
                }
            }
            return ans;
        }
    };
    

    单周赛 (239)

    到目标元素的最小距离

    题意

    给定一个整数数组 nums,以及两个整数 target, start,找出一个下标 i,满足 nums[i] == target 同时 abs(i - start) 最小化

    题解

    签到,按题意模拟

    class Solution {
    public:
        int getMinDistance(vector<int>& nums, int target, int start) {
            int n = nums.size();
            int ans = INT_MAX;
            for (int i = 0; i < n; ++i)
                if (target == nums[i]) ans = min(ans, abs(start - i));
            return ans;
        }
    };
    

    将字符串拆分为递减的连续值

    题意

    给定一个仅由数字组成的字符串 s

    请判断能否将 s 分割成两个或多个非空子串(连续子序列,使得子串的 数字 按照 降序 排列,且相邻数字的差为 1

    • 例如 009008 可以划分成 9, 8
    • 例如 200100 可以划分成 2, 1
    • 例如 1234 无法被划分

    (1leq nleq 20)

    题解

    数据很小,考虑搜索

    pre 记录之前的值,cur 记录当前的值,cnt 记录划分的子串个数

    每一层 dfs 枚举隔板的位置,具体来说,如果 cnt == 0 或者 pre == cur + 1,就可以防止隔板,进行新一轮的划分

    数据很大,得用高科技

    细节较多,好想不好写

    #define ull unsigned long long
    class Solution {
    public:
        bool dfs(ull pre, int p, string s, int cnt)
        {
            if (p == s.length()) return cnt > 1;
            ull cur = 0;
            for (int i = p; i < s.length(); ++i) {
                cur = cur * 10 + s[i] - '0';
                if (!cnt || cur == pre - 1) { // 符合条件,就试一下能不能继续划分
                    if (dfs(cur, i + 1, s, cnt + 1))
                        return 1;
                }
            }
            return 0;
        }
        bool splitString(string s) {
            return dfs(0, 0, s, 0);
        }
    };
    

    邻位交换的最小次数

    题意

    给一个表示大整数的字符串 num,和一个整数 k

    如果某个整数是 num 中各位数字的一个排列,且它的值大于 num,则称这个整数为 妙数,我们关注值 最小 的妙数

    返回要得到第 k最小妙数,需要对 num 执行的相邻位数字交换的最小次数。

    题解

    最小妙数可以用 next_permutation 解决

    下面考虑最小交换次数,即为 排列的距离

    考虑 各位不同 的整数 p,以及其排列 q,计算从 pq 相邻数字的最小交换次数

    有一个结论,若要使得任意排列有序,那么相邻数字交换的最小次数为逆序数

    作下标映射 p[i] -> i,那么 p 映射成 1, 2, 3..., n,再计算出 q 中对应的下标,求出 q 的逆序数即可

    例如,p = 4312, q = 1243 ,作下标映射,那么 p = 1234, q = 3412,得到逆序数为 4

    考虑 存在相同位,令相同位的 相对顺序不改变,例如 12322 -> 32122123222 的下标分别为 2, 4, 5,那么 32122 中对应的下标为 32145,逆序数为 3

    本题带点思维含量,知道逆序数的结论就好做了

    #define pb push_back
    class Solution {
    public:
        int getInvNum(vector<int> s)
        {
            int n = s.size();
            int num = 0;
            for (int i = 0; i < n; ++i) {
                for (int j = i + 1; j < n; ++j) {
                    if (s[i] > s[j]) num++;
                }
            }
            return num;
        }
        int getMinSwaps(string num, int k) {
            string s = num;
            int n = num.length();
            while (k--) next_permutation(s.begin(), s.end());
            vector<int> mp[10];
            for (int i = 0; i < n; ++i)
                mp[s[i] - '0'].pb(i);
            vector<int> a(n);
            vector<int> idx(10);
            for (int i = 0; i < n; ++i) {
                a[i] = mp[num[i] - '0'][idx[num[i] - '0']];
                idx[num[i] - '0']++;
            }
            return getInvNum(a);
        }
    };
    

    包含每个查询的最小区间

    题意

    给定一个二维数组 intervals,每个元素是个二元组 (left, right),表示一个 闭区间

    给定一个询问数组 queries,第 j 个查询的答案是满足 lefti <= queries[j] <= righti 的长度最小的区间 i 的长度。如果不存在这样的区间,那么答案是 -1

    (1leq intervals.sizeleq 100,000)

    (1leq queries.sizeleq 100,000)

    题解

    本题和 最远的房间 有异曲同工之妙,具体维护一个存放区间长度和区间右端点的 单调队列

    intervalsqueries 排序,考虑 顺序枚举查询,动态维护单调队列 set<pair<int, int>>,其中 pair.first 记录区间长度,pair.second 记录区间右端点

    • 考虑入队,设区间长度 Li = intervals[i][1] - intervals[i][0] + 1,利用双指针,将所有满足 intervals[i][0] <= queries[j]Li, intervals[i][1] 打包成二元组放入 set。由于是顺序枚举,后面的查询值一定大于队列中所有区间的左端点
    • 考虑出队,需要把队列中所有 set.begin()->second < queries[j] 的二元组剔除,因为他们的右端点小于目标值
    • 考虑查询,返回 set.begin()->first

    每个区间最多入队一次,出队一次,单次复杂度为 (O(logn)),因此总的时间复杂度为 (O(nlogn))

    #define pb push_back
    class Solution
    {
    public:
        vector<int> minInterval(vector<vector<int>> &intervals, vector<int> &queries)
        {
            int n = intervals.size(), k = queries.size();
            vector<int> idx(k), ans(k);
            for (int i = 0; i < k; ++i) idx[i] = i;
            sort(idx.begin(), idx.end(), [&](int x, int y)
            {
                return queries[x] < queries[y];
            });
            sort(intervals.begin(), intervals.end());
            set<pair<int, int>> st;
            int j = 0;
            for (auto &i: idx) {
                int tar = queries[i];
                while (j < n && intervals[j][0] <= tar) {
                    st.emplace(intervals[j][1] - intervals[j][0] + 1, intervals[j][1]);
                    ++j;
                    
                }
                while (!st.empty() && st.begin()->second < tar)
                    st.erase(st.begin());
                if (st.empty()) ans[i] = -1;
                else ans[i] = st.begin()->first;
            }
            return ans;
        }
    };
    
  • 相关阅读:
    性能测试学习第五天-----Jmeter测试脚本&基础元件使用
    【win10主机】连接virtualbox上【32位winXP系统虚拟机】上启动的mysql
    【win10主机】访问virtualbox上【32位winXP系统虚拟机】上启动的项目
    性能测试学习第四天-----loadrunner:jdbc批量制造测试数据 & controller应用
    appium输入法踩坑解决方案-----中文乱码及输入法搜索无法点击
    性能测试学习第三天-----loadrunner接口测试&中文乱码处理
    Ext JS学习第四天 我们所熟悉的javascript(三)
    Ext JS学习第五天 我们所熟悉的javascript(四)
    ExtJS学习第一天 MessageBox
    C#使用Zxing2.0生成二维码 带简单中心LOGO
  • 原文地址:https://www.cnblogs.com/ChenyangXu/p/14726575.html
Copyright © 2020-2023  润新知