LeetCode 力扣周赛 273

150 阅读2分钟

5963. 反转两次的数字

思路:检查个位数

时间复杂度O(1)\mathrel{O}(1)

空间复杂度O(1)\mathrel{O}(1)

只要个位数是零,就会导致第一次翻转后出现前导零,进而导致第二次翻转后丢失前导零。反之,个位数非零则两次翻转后不会发生变化。特别的,需要单独考虑数字 00

class Solution {
public:
    bool isSameAfterReversals(int num) {
        if (num != 0 && num%10 == 0) {
            return false;
        }
        return true;
    }
};

5964. 执行所有后缀指令

思路:暴力枚举,模拟。

时间复杂度O(n2)\mathrel{O}(n^2)

空间复杂度O(1)\mathrel{O}(1)。不包含存储答案的空间开销。

枚举起始位置 ii,然后机器人从初始位置出发,依次执行 nin-i 条命令。在执行过程中,检测机器人的位置是否合法即可。

class Solution {
public:
    int walk(int n, int x, int y, const char *cmd) {
        int i = 0;
        for (i = 0; cmd[i] != '\0'; i++) {
            int dx = 0, dy = 0;
            // 计算当前指令的位移方向
            switch (cmd[i]) {
                case 'L': dy = -1; break; 
                case 'R': dy = 1; break;
                case 'U': dx = -1; break;
                case 'D': dx = 1; break;
            }
            // 计算下一个位置
            int nx = x + dx, ny = y + dy;
            // 检查下一个位置是否合法
            if (0 <= nx && nx < n && 0 <= ny && ny < n) {
                x = nx, y = ny;
            } else {
                return i;
            }
        }
        return i;
    }
    vector<int> executeInstructions(int n, vector<int>& startPos, string s) {
        // anw 用于存储答案
        vector<int> anw;
        for (int i = 0; i < s.size(); i++) {
            // 枚举执行命令的起始位置
            anw.push_back(walk(n, startPos[0], startPos[1], s.c_str() + i));
        }
        return anw;
    }
};

5965. 相同元素的间隔之和

思路:前缀和

时间复杂度O(n)\mathrel{O}(n)

空间复杂度O(n)\mathrel{O}(n)

考虑位置 ii,如果我们知道

  • 在区间 [0,i)[0,i) 中,数字 arriarr_i 出现的次数 cnti,0cnt_{i,0} 及其位置之和 sumi,0sum_{i,0}
  • 在区间 (i,n)(i,n) 中,数字 arriarr_i 出现的次数 cnti,1cnt_{i,1} 及其位置之和 sumi,1sum_{i,1}

则位置 ii 的间隔之和可表示为:

(cnti,0isumi,0)+(sumi,1cnti,1i)(cnt_{i,0} * i - sum_{i,0}) + (sum_{i,1} - cnt_{i,1} * i)

因此,我们我们只需

  • 正向遍历一次 arrarr,统计出所有的 cnti,0cnt_{i,0}sumi,0sum_{i,0},并计算 cnti,0isumi,0cnt_{i,0} * i - sum_{i,0}
  • 逆序遍历一次 arrarr,统计出所有的 cnti,1cnt_{i,1}sumi,1sum_{i,1},并计算 sumi,1cnti,1isum_{i,1} - cnt_{i,1} * i

详见注释~

class Solution {
public:
    vector<long long> getDistances(vector<int>& arr) {
        // mark.first 表示arr中出现的数字
        // mark.second.first 表示位置之和
        // mark.second.second 表示出现次数
        unordered_map<int, pair<int64_t, int64_t>> mark;
        // anw 用于存储答案
        vector<long long> anw(arr.size());
        
        // 开始正序遍历
        for (int i = 0; i < arr.size(); i++) {
            auto &data = mark[arr[i]];
            // 从 mark 中取出 sum_{i,0} 和 cnt_{i,0},更新 anw[i]
            anw[i] += data.second * 1L * i - data.first;
            // 更新 sum_{i,0} 和 cnt_{i,0}
            data.second ++;
            data.first += i;
        }
        // 重置  mark,以便在逆序遍历中使用
        mark.clear();
        // 开始逆序遍历
        for (int i = arr.size()-1; i >= 0; --i) {
            auto &data = mark[arr[i]];
            // 从 mark 中取出 sum_{i,1} 和 cnt_{i,1},更新 anw[i]
            anw[i] += data.first - data.second * 1L * i;
            // 更新 sum_{i,1} 和 cnt_{i,1}
            data.second ++;
            data.first += i;
        }
        return anw;
    }
};

5966. 还原原数组

思路:枚举 kk

时间复杂度O(n2)\mathrel{O}(n^2)

空间复杂度O(n)\mathrel{O}(n)

假设已知 kk,则可按下述思路构造答案 arrarr

numsnums 中的最小值一定在 lowerlower 中,因此可从 numsnums 中删除最小值 xx 以及 x+2kx+2k,并将 x+kx+k 放入 arrarr 中。重复这个过程,直到 numsnums 变为空。借助哈希表,这个过程的时间复杂度可以做到 O(n)\mathrel{O}(n)

但现在我们不知道 kk,因此需先找到所有可能的 kk。先将 numsnums 排序,然后判断 numsinums0nums_i-nums_0 是否大于零且能被 2 整除。如果可以,则 numsinums02\frac{nums_i-nums_0}{2} 即是一个可能的 kk

因为 kk 的候选值不会超过 nn 个,所以我们可枚举所有的候选值,然后尝试构造。整体的时间复杂度为 O(n2)\mathrel{O}(n^2)

详见注释~

class Solution {
public:
    vector<int> recoverArray(vector<int>& nums) {
        // 寻找 k 的候选值,并存储到 cand 中。
        sort(nums.begin(), nums.end());
        unordered_set<int> cand;
        for (int i = 1; i < nums.size(); i++) {
            if (((nums[i] - nums[0])&1) == 0 && nums[i] != nums[0]) {
                cand.insert((nums[i]-nums[0])/2);
            }
        }
        // anw 用于存储答案
        vector<int> anw;
        // 构造哈希表,以便实现 O(n) 构造过程。
        unordered_multiset<int> mark1;
        for (auto num : nums) {
            mark1.insert(num);
        }
        // 枚举 k
        for (auto k : cand) {
            // 初始化 anw 和 mark,用于一次构造过程
            unordered_multiset<int> mark = mark1;
            anw.resize(0);
            anw.reserve(nums.size()/2);
            // 从小到大枚举 nums
            for (int i = 0; i < nums.size(); i++) {
                int num = nums[i];
                auto fit = mark.find(num);
                // 如果 num 不在 mark 中,说明已经被删除了,继续下一个。
                if (fit == mark.end()) { continue; }
                // num 在 mark 中,则 num+2k 必须在 mark 中。即 num 属于 lower,num+2k 属于 higher
                auto sit = mark.find(num + 2*k);
                if (sit == mark.end()) {
                    break;
                }
                // 删除 num 和 num+2k,并将 num+k 放入 anw 中。
                anw.push_back(num + k);
                mark.erase(fit);
                mark.erase(sit);
            }
            // 构造成功,返回答案。否则继续处理下一个候选值
            if (anw.size() == nums.size()/2) {
                return anw;
            }
        }
        // 题目保证了一定有答案,因此不会执行到这。加这个只是为了消除编译报警。
        return vector<int>{};
    }
};