Leetcode 373周赛

周赛链接:https://leetcode.cn/contest/weekly-contest-373/

100139. 循环移位后的矩阵相似检查

不需要判断奇数还是偶数,题目要求最后两个矩阵是否相同,那么向左循环移动和向右循环移动意义是一样的

奇数行右移k次,$$a[i]==a[(i + k) % size]$$

偶数行左移k次,$$a[i] == a[(i - k) % size]$$

向右循环移动即可,如果某个位置不相同,直接返回false

class Solution {
public:
    bool areSimilar(vector<vector<int>>& mat, int k) {
        bool flag = true;
        for (auto row : mat) {
            for (int i = 0; i < row.size(); i ++) {
                if (row[i] != row[(i + k) % row.size()]) {
                    return false;
                }
            }
        }
        return flag;
    }
};

100142. 交换得到字典序最小的数组

image-20231126170232811

模拟,找规律,找联系。

对于每个数字,如果存在另外一个数字,跟他相差,小于等于limit,那么就是一个连通块,这样就能互相交换。

把nums排序后,需要找到一个最长的连续段,相邻数字的差是 <= limit

这一整段对应的下标集合,这些位置之间是可以随意排序的。

本题用到一个经典结论。

把序列中的每个元素看成图中的一个点,如果两个元素可以相互交换,则在两个元素之间连一条边。结论:处于同一连通块内的所有元素可以通过若干次操作排成任意顺序。

排序

image-20231126170352069

把原序列,从小到大排好序,为了让答案的字典序最小,每个子段从小到大排序即可。

参考题解

https://leetcode.cn/problems/make-lexicographically-smallest-array-by-swapping-elements/solutions/2542306/pai-xu-by-tsreaper-a8wl/

class Solution {
public:
    vector<int> lexicographicallySmallestArray(vector<int>& nums, int limit) {
        int n = nums.size();
        typedef pair<int, int> pii;
        // 把所有元素从小到大排序,要记录一下原来的下标
        vector<pii> vec;
        for (int i = 0; i < n; i ++) vec.push_back(pii(nums[i], i));
        sort(vec.begin(), vec.end());

        // for (auto x : vec) {
        //     cout << x.first << ' ';
        // }
        // cout << endl;

        // 把所有元素切成若干子段,子段内的相邻元素之差不超过 limit
        /**
        * segs 二维数组
        **/
        vector<vector<pii>> segs;
        int last = -limit;
        for (int i = 0; i < n; i ++) {
            if (vec[i].first - last > limit) segs.push_back({}); // 这里插入一个空行,切割
            segs.back().push_back(vec[i]);
            last = vec[i].first; // 更新
        }

        vector<int> ans(n);
        // 对每个子段分别从小到大排序,填回序列中
        for (auto &seg : segs) {
            vector<int> pos; // 每次新建一个数组,保存当前这层的下标
            for (auto &p : seg) pos.push_back(p.second); // 把每一组的下标保存好
            sort(pos.begin(), pos.end()); // seg 里面,每个组都是有序的,这里把下标排好序,让每个分组都可以互相插入进去
            // 这样整体字典序就最小了
            // for (auto x : pos) {
            //     cout << x << ' ';
            // }
            // cout << endl;
            for (int i = 0; i < seg.size(); i ++) ans[pos[i]] = seg[i].first;
        }


        return ans;
    }
};

100134. 统计美丽子字符串 I

100132. 统计美丽子字符串 II

参考题解:

https://leetcode.cn/problems/count-beautiful-substrings-ii/solutions/2542302/mei-ju-by-tsreaper-1fi2/

class Solution {
public:
    long long beautifulSubstrings(string s, int K) {
        // 判断字符 c 是不是元音
        auto check = [&](char c) {
            return c == 'a' || c == 'e' || c == 'i' || c == 'o'
                || c == 'u';
        };
        int n = s.size();

        // valid 里保存所有满足 w^2 mod k = 0 的 w,这里是暴力枚举。
        vector<int> valid;
        for (int rem = 0; rem < K; rem ++) {
            if (rem * rem % K == 0) valid.push_back(rem);
        }
        long long ans = 0;
        unordered_map<int, unordered_map<int ,int>> mp;
        // 用长度为 0 的前缀初始化哈希表
        mp[0][0] = 1;
        // det: 前缀中元音 - 辅音的值
        // cnt: 前缀中元音的数量
        int det = 0, cnt = 0;
        // 枚举子串右端点
        for (int i = 0; i < n;i ++) {
            if (check(s[i])) det ++, cnt ++;
            else det --;
            // 枚举 w
            for (int rem : valid) {
                int t = (cnt - rem + K) % K;
                if (mp[det].count(t)) ans += mp[det][t];
            }
            // 用以 i 为结尾的前缀更新hash表
            mp[det][cnt % K] ++;
        }
        return ans;
    }
};
posted @ 2023-11-26 18:43  lukelmouse  阅读(10)  评论(0编辑  收藏  举报