【第 88 场双周赛】逆序对(离散化 + 树状数组)、异或性质的运用

周赛链接:
https://leetcode.cn/contest/biweekly-contest-88/


A. 2423. 删除字符使频率相同

给你一个下标从 0 开始的字符串 word ,字符串只包含小写英文字母。你需要选择 一个 下标并 删除 下标处的字符,使得 word 中剩余每个字母出现 频率 相同。

如果删除一个字母后,word 中剩余所有字母的出现频率都相同,那么返回 true ,否则返回 false 。

注意:

字母 x 的 频率 是这个字母在字符串中出现的次数。
你 必须 恰好删除一个字母,不能一个字母都不删除。

对于这个坑题,真的不应该面相用例编程,交了2发WA,就应该直接暴力解题。

AC代码:

class Solution {
public:
    bool equalFrequency(string word) {
        vector<int> s(26, 0);
        for (auto c : word)
            s[c - 'a'] ++;
        
        map<int, int> m;
        for (auto c : s)
        {
            if (c == 0) continue;
            m[c] ++;
        }
        if (m.size() == 2) {
            int maxn = -1, cnt;
            for (auto it = m.begin(); it != m.end(); it ++)
            {
                if (maxn == -1) maxn = it -> first, cnt = it -> second;
                else if ((it -> second == 1 || cnt == 1) && abs(it -> first - maxn) == 1) return true;
            }
        } else if (m.size() == 1) {
            if (m.begin()->first == 1 || m.begin()->second == 1) return true;
        }
        return false;
    }
};

B. 2424. 最长上传前缀

题目描述:
给你一个 n 个视频的上传序列,每个视频编号为 1 到 n 之间的 不同 数字,你需要依次将这些视频上传到服务器。请你实现一个数据结构,在上传的过程中计算 最长上传前缀 。

如果 闭区间 1 到 i 之间的视频全部都已经被上传到服务器,那么我们称 i 是上传前缀。最长上传前缀指的是符合定义的 i 中的 最大值 。

请你实现 LUPrefix 类:

LUPrefix(int n) 初始化一个 n 个视频的流对象。
void upload(int video) 上传 video 到服务器。
int longest() 返回上述定义的 最长上传前缀 的长度。

数据结构设计题,video的范围是1~1e5,所以用数组模拟哈希就可以,不需要用unordered_map

AC代码:

class LUPrefix {
public:
    int maxn = 0;
    unordered_map<int, int> m;
    LUPrefix(int n) {
        m.clear();
        maxn = 0;
    }
    
    void upload(int video) {
        m[video] = 1;
        if (maxn + 1 == video)
        {
            int t = video;
            while(m.count(t) == 1)
            {
                t ++;
            }
            maxn = t - 1;
        }
    }
    
    int longest() {
        return maxn;
    }
};

/**
 * Your LUPrefix object will be instantiated and called as such:
 * LUPrefix* obj = new LUPrefix(n);
 * obj->upload(video);
 * int param_2 = obj->longest();
 */

C. 2425. 所有数对的异或和

题目描述:
给你两个下标从 0 开始的数组 nums1 和 nums2 ,两个数组都只包含非负整数。请你求出另外一个数组 nums3 ,包含 nums1 和 nums2 中 所有数对 的异或和(nums1 中每个整数都跟 nums2 中每个整数 恰好 匹配一次)。
请你返回 nums3 中所有整数的 异或和 。

这道题要到异或的两个性质:

  • 交换律
  • a ^ a = 0, 0 ^ a = a

举例说明:
nums1 = [a, b, c]
nums2 = [d, e]
nums3 = [a ^ d, a ^ e, b ^ d, b ^ e, c ^ d, c ^ e]
(a ^ d) ^ (a ^ e) ^ (b ^ d) ^ (b ^ e) ^ (c ^ d) ^ (c ^ e) = a ^ d ^ a ^ e ^ b ^ d ^ b ^ e ^ c ^ d ^ c ^ e
= (a ^ a ^ b ^ b ^ c ^ c ^ d ^ d ^ d ^ e ^ e ^ e)
= (d ^ e)

AC代码:

class Solution {
public:
    int xorAllNums(vector<int>& nums1, vector<int>& nums2) {
        
        int res = 0;
        
        if (nums1.size() & 1)
            for (int i = 0; i < nums2.size(); i ++)
                res ^= nums2[i];
        if (nums2.size() & 1)
            for (int i = 0; i < nums1.size(); i ++)
                res ^= nums1[i];
        return res;
    }
};

D. 2426. 满足不等式的数对数目

题目描述:
给你两个下标从 0 开始的整数数组 nums1 和 nums2 ,两个数组的大小都为 n ,同时给你一个整数 diff ,统计满足以下条件的 数对 (i, j) :

0 <= i < j <= n - 1 且
nums1[i] - nums1[j] <= nums2[i] - nums2[j] + diff.
请你返回满足条件的 数对数目 。

首先可以将原始公式进行化简:
nums1[i] - nums1[j] <= nums2[i] - nums2[j] + diff. ->
nums1[i] - nums2[i] <= nums1[j] - nums2[j] + diff.
设nums[i] = nums1[i] - nums2[i]
则原始公式变为
nums[i] <= nums[j] + diff
那么这个问题就转换为了经典的逆序对问题了,常用的处理方法有:

  • 归并排序
  • 离散化 + 树状数组。

可以参考官方题解数组中的逆序对

下边的代码使用的是离散化 + 树状数组的方法。
AC代码:

typedef long long LL;
class Solution {
    vector<int> c;
    int tr[200010];
    int n;
    void add(int i, int x) {
        for (; i <= n; i += i & (-i)) tr[i] += x;
    }
    LL sum(int i) {
        int ans = 0;
        for (; i; i -= i & (-i)) ans += tr[i];
        return ans;
    }
    int get(int x) {
        return lower_bound(c.begin(), c.end(), x) - c.begin() + 1;
    }
public:
    long long numberOfPairs(vector<int>& nums1, vector<int>& nums2, int diff) {
        int len = nums1.size();
        for (int i = 0; i < len; i ++)
        {
            nums1[i] -= nums2[i];
            c.push_back(nums1[i]);
            c.push_back(nums1[i] + diff);
        }
        
        sort(c.begin(), c.end());
        c.erase(unique(c.begin(), c.end()), c.end());
        n = c.size();

        LL res = 0;

        for (int i = 0; i < len; i ++)
        {
            res += sum(get(nums1[i] + diff));
            add(get(nums1[i]), 1);
        }

        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Honyelchak

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值