【LeetCode周赛】LeetCode第371场周赛

找出强数对的最大异或值 I

给你一个下标从 0 开始的整数数组 nums 。如果一对整数 x 和 y 满足以下条件,则称其为 强数对 :
|x - y| <= min(x, y)
你需要从 nums 中选出两个整数,且满足:这两个整数可以形成一个强数对,并且它们的按位异或(XOR)值是在该数组所有强数对中的 最大值 。
返回数组 nums 所有可能的强数对中的 最大 异或值。
注意,你可以选择同一个整数两次来形成一个强数对。

示例 1:

输入:nums = [1,2,3,4,5]
输出:7
解释:数组 nums 中有 11 个强数对:(1, 1), (1, 2), (2,2), (2, 3), (2, 4), (3, 3), (3, 4), (3, 5), (4, 4), (4, 5) 和 (5, 5) 。
这些强数对中的最大异或值是 3 XOR 4 = 7 。

示例 2:

输入:nums = [10,100]
输出:0
解释:数组 nums 中有 2 个强数对:(10, 10) 和 (100, 100) 。这些强数对中的最大异或值是 10 XOR 10 = 0 ,数对 (100, 100) 的异或值也是 100 XOR 100 = 0 。

示例 3:

输入:nums = [5,6,25,30]
输出:7
解释:数组 nums 中有 6 个强数对:(5, 5), (5, 6), (6,6), (25, 25), (25, 30) 和 (30, 30) 。 这些强数对中的最大异或值是 25 XOR 30 = 7;另一个异或值非零的数对是 (5, 6) ,其异或值是 5 XOR 6 = 3 。

提示:
1 < = n u m s . l e n g t h < = 50 1 <= nums.length <= 50 1<=nums.length<=50
1 < = n u m s [ i ] < = 100 1 <= nums[i] <= 100 1<=nums[i]<=100

分析:
本题数据范围很小,可以暴力遍历所有的数对,判断满足|x - y| <= min(x, y)条件的最大的x,y异或值即可。

代码:

class Solution {
public:
    int maximumStrongPairXor(vector<int>& nums) {
        int n = nums.size();
        int ans = 0;
        for(int i = 0; i < n ;i++){
            int x = nums[i];
            for(int j = 0; j < n; j++){
                int y = nums[j];
                if(abs(x-y) <= min(x, y)){
                    ans = max(ans, x ^ y);
                }
            }
        }
        return ans;
    }
};

高访问员工

给你一个长度为 n 、下标从 0 开始的二维字符串数组 access_times 。对于每个 i(0 <= i <= n - 1 ),access_times[i][0] 表示某位员工的姓名,access_times[i][1] 表示该员工的访问时间。access_times 中的所有条目都发生在同一天内。
访问时间用 四位 数字表示, 符合 24 小时制 ,例如 “0800” 或 “2250” 。
如果员工在 同一小时内 访问系统 三次或更多 ,则称其为 高访问 员工。
时间间隔正好相差一小时的时间 不 被视为同一小时内。例如,“0815” 和 “0915” 不属于同一小时内。
一天开始和结束时的访问时间不被计算为同一小时内。例如,“0005” 和 “2350” 不属于同一小时内。
以列表形式,按任意顺序,返回所有 高访问 员工的姓名。

示例 1:

输入:access_times =[[“a”,“0549”],[“b”,“0457”],[“a”,“0532”],[“a”,“0621”],[“b”,“0540”]]
输出:[“a”]
解释:“a” 在时间段 [05:32, 06:31] 内有三条访问记录,时间分别为 05:32 、05:49 和06:21 。 但是 “b” 的访问记录只有两条。 因此,答案是 [“a”] 。

示例 2:

输入:access_times =[[“d”,“0002”],[“c”,“0808”],[“c”,“0829”],[“e”,“0215”],[“d”,“1508”],[“d”,“1444”],[“d”,“1410”],[“c”,“0809”]]
输出:[“c”,“d”]
解释:“c” 在时间段 [08:08, 09:07] 内有三条访问记录,时间分别为 08:08 、08:09 和08:29 。 “d” 在时间段 [14:10, 15:09] 内有三条访问记录,时间分别为 14:10 、14:44 和 15:08 。然而,“e” 只有一条访问记录,因此不能包含在答案中,最终答案是 [“c”,“d”] 。

示例 3:

输入:access_times =[[“cd”,“1025”],[“ab”,“1025”],[“cd”,“1046”],[“cd”,“1055”],[“ab”,“1124”],[“ab”,“1120”]]
输出:[“ab”,“cd”]
解释:"ab"在时间段 [10:25, 11:24] 内有三条访问记录,时间分别为 10:25 、11:20和 11:24 。 “cd” 在时间段 [10:25, 11:24] 内有三条访问记录,时间分别为 10:25 、10:46 和 10:55。 因此,答案是 [“ab”,“cd”] 。

提示:
1 < = a c c e s s _ t i m e s . l e n g t h < = 100 1 <= access\_times.length <= 100 1<=access_times.length<=100
a c c e s s _ t i m e s [ i ] . l e n g t h = = 2 access\_times[i].length == 2 access_times[i].length==2
1 < = a c c e s s _ t i m e s [ i ] [ 0 ] . l e n g t h < = 10 1 <= access\_times[i][0].length <= 10 1<=access_times[i][0].length<=10
a c c e s s _ t i m e s [ i ] [ 0 ] access\_times[i][0] access_times[i][0] 仅由小写英文字母组成。
a c c e s s _ t i m e s [ i ] [ 1 ] . l e n g t h = = 4 access\_times[i][1].length == 4 access_times[i][1].length==4
a c c e s s _ t i m e s [ i ] [ 1 ] access\_times[i][1] access_times[i][1] 采用24小时制表示时间。
a c c e s s _ t i m e s [ i ] [ 1 ] access\_times[i][1] access_times[i][1] 仅由数字 ‘0’ 到 ‘9’ 组成。

分析:
这道题是一个简单模拟题,按照题目的意思,我们可以先按照员工的姓名,存储一下每个员工的访问时间,然后判断在每一个小时内,员工访问次数是否达到了三次即可。可以使用 u n o r d e r e d _ m a p < s t r i n g , v e c t o r < s t r i n g > > unordered\_map<string,vector<string>> unordered_map<string,vector<string>>来存储这些信息。

代码:

class Solution {
public:
    int sub(string a, string b){
        int h1 = atoi(a.substr(0, 2).c_str()), h2 = atoi(b.substr(0, 2).c_str());
        int m1 = atoi(a.substr(2, 2).c_str()), m2 = atoi(b.substr(2, 2).c_str());
        return (h2 - h1) * 60 + (m2 - m1);
    }
    vector<string> findHighAccessEmployees(vector<vector<string>>& access_times) {
        unordered_map<string, vector<string>>mp;
        for(auto access_time: access_times){
            string a = access_time[0];
            string time = access_time[1];
            mp[a].push_back(time);
        }
        vector<string>ans;
        for(auto &[k, v]: mp){
            sort(v.begin(), v.end());
            int n = v.size();
            bool flag = false;
            if(n < 3)continue;
            for(int i = 0; i < n - 2; i++){
                if(sub(v[i], v[i + 2]) < 60)flag = true;
            }
            if(flag)ans.push_back(k);
        }
        return ans;
    }
};

最大化数组末位元素的最少操作次数

给你两个下标从 0 开始的整数数组 nums1 和 nums2 ,这两个数组的长度都是 n 。
你可以执行一系列 操作(可能不执行)。
在每次操作中,你可以选择一个在范围 [0, n - 1] 内的下标 i ,并交换 nums1[i] 和 nums2[i] 的值。
你的任务是找到满足以下条件所需的 最小 操作次数:
nums1[n - 1] 等于 nums1 中所有元素的 最大值 ,即 nums1[n - 1] = max(nums1[0], nums1[1], …, nums1[n - 1]) 。
nums2[n - 1] 等于 nums2 中所有元素的 最大值 ,即 nums2[n - 1] = max(nums2[0], nums2[1], …, nums2[n - 1]) 。
以整数形式,表示并返回满足上述 全部 条件所需的 最小 操作次数,如果无法同时满足两个条件,则返回 -1 。

示例 1:

输入:nums1 = [1,2,7],nums2 = [4,5,3]
输出:1
解释:在这个示例中,可以选择下标 i = 2 执行一次操作。交换 nums1[2] 和 nums2[2] 的值,nums1 变为 [1,2,3] ,nums2 变为 [4,5,7] 。同时满足两个条件。 可以证明,需要执行的最小操作次数为 1 。 因此,答案是 1 。

示例 2:

输入:nums1 = [2,3,4,5,9],nums2 = [8,8,4,4,4]
输出:2
解释:在这个示例中,可以执行以下操作:首先,选择下标 i = 4 执行操作。 交换 nums1[4] 和 nums2[4] 的值,nums1 变为 [2,3,4,5,4],nums2 变为 [8,8,4,4,9] 。 然后,选择下标 i = 3 执行操作。 交换 nums1[3] 和 nums2[3]的值,nums1 变为 [2,3,4,4,4] ,nums2 变为 [8,8,4,5,9] 。 同时满足两个条件。可以证明,需要执行的最小操作次数为 2 。 因此,答案是 2 。

示例 3:

输入:nums1 = [1,5,4],nums2 = [2,5,3]
输出:-1
解释:在这个示例中,无法同时满足两个条件。 因此,答案是-1 。

提示:
1 < = n = = n u m s 1. l e n g t h = = n u m s 2. l e n g t h < = 1000 1 <= n == nums1.length == nums2.length <= 1000 1<=n==nums1.length==nums2.length<=1000
1 < = n u m s 1 [ i ] < = 1 0 9 1 <= nums1[i] <= 10^9 1<=nums1[i]<=109
1 < = n u m s 2 [ i ] < = 1 0 9 1 <= nums2[i] <= 10^9 1<=nums2[i]<=109

分析:
通过分析题意,我哦们可以知道,每次交换只能交换两个数组的相同位置。那么每一个数组的最后一个位置,要么是自身的最后一个数字,要么就是另一个数组的最后一个位置的数。所以我们可以直接枚举这两种情况,当确定了最后一个位置时,其他的位置,只要比最后一个数字大,那么就要和另一个数组交换位置,但是如果交换完位置后,还是比最后一个数字大,则当前情况不满足,直接按照题目意思模拟两种情况即可。

代码:

class Solution {
public:
    int min_op(vector<int> nums1, vector<int> nums2){
        int n = nums1.size();
        bool flag = true;
        int cnt = 0;
        for(int i = 0; i < n - 1; i++){
            if(nums1[i] > nums1[n - 1]){
                swap(nums1[i],nums2[i]);
                cnt++;
                if(nums1[i] > nums1[n - 1]){
                    flag = false;
                    break;
                }
            }
        }
        for(int i = 0; i < n - 1; i++){
            if(nums2[i] > nums2[n - 1]){
                swap(nums1[i],nums2[i]);
                cnt++;
                if(nums2[i] > nums2[n - 1] || nums1[i] > nums1[n - 1]){
                    flag = false;
                    break;
                }
            }
        }
        if(!flag)cnt = INT_MAX;
        return cnt;
    }
    int minOperations(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size();
        vector<int>tmp1, tmp2;
        tmp1.assign(nums1.begin(), nums1.end());
        tmp2.assign(nums2.begin(), nums2.end());
        //不交换最后一个
        int cnt1 = min_op(tmp1, tmp2);
        swap(nums1[n - 1], nums2[n - 1]);
        int cnt2 = min_op(nums1, nums2);
        if(cnt2 != INT_MAX) cnt2 += 1;
        int res = min(cnt1, cnt2);
        if(res == INT_MAX)res = -1;
        return res;
    }
};

找出强数对的最大异或值 II

给你一个下标从 0 开始的整数数组 nums 。如果一对整数 x 和 y 满足以下条件,则称其为 强数对 :
|x - y| <= min(x, y)
你需要从 nums 中选出两个整数,且满足:这两个整数可以形成一个强数对,并且它们的按位异或(XOR)值是在该数组所有强数对中的 最大值 。
返回数组 nums 所有可能的强数对中的 最大 异或值。
注意,你可以选择同一个整数两次来形成一个强数对。

示例 1:

输入:nums = [1,2,3,4,5]
输出:7
解释:数组 nums 中有 11 个强数对:(1, 1), (1, 2), (2,2), (2, 3), (2, 4), (3, 3), (3, 4), (3, 5), (4, 4), (4, 5) 和 (5, 5) 。
这些强数对中的最大异或值是 3 XOR 4 = 7 。

示例 2:

输入:nums = [10,100]
输出:0
解释:数组 nums 中有 2 个强数对:(10, 10) 和 (100, 100) 。这些强数对中的最大异或值是 10 XOR 10 = 0 ,数对 (100, 100) 的异或值也是 100 XOR 100 = 0 。

示例 3:

输入:nums = [5,6,25,30]
输出:7
解释:数组 nums 中有 6 个强数对:(5, 5), (5, 6), (6,6), (25, 25), (25, 30) 和 (30, 30) 。 这些强数对中的最大异或值是 25 XOR 30 = 7;另一个异或值非零的数对是 (5, 6) ,其异或值是 5 XOR 6 = 3 。

提示:
1 < = n u m s . l e n g t h < = 5 ∗ 1 0 4 1 <= nums.length <= 5 * 10^4 1<=nums.length<=5104
1 < = n u m s [ i ] < = 2 20 − 1 1 <= nums[i] <= 2^{20} - 1 1<=nums[i]<=2201

分析:
这道题为第一道题的数据加强版,对 ∣ x − y ∣ ≤ m i n ( x , y ) |x - y| \le min(x, y) xymin(x,y)条件,我们仔细分析,即 x   l e y x \ le y x ley时, y ≤ 2 x y \le 2x y2x x ≥ y x \ge y xy时, x ≤ 2 y x \le 2y x2y。所以对于每一个数字 x x x,我们找到其在 [ x , 2 x ] [x,2x] [x,2x]范围内的的一个数字 y y y,使得 x x x^ y y y的值最大。
但是我们还是不好直接暴力求解。那么我们可以考虑每一个比特位,对每一个比特位来说,该位置是否可以为1。
具体来说,用一个掩码mask,每一次只考虑前i位,将后面的都置0,因为如果 a n s = x ans = x ans=x^ y y y,那么有 y = a n s y = ans y=ans^ x x x,根据这一点,先将每一个y变为mask_y,即第i位后面的数都置0。我们假设当前可以构成new_ans这个结果。然后去寻找是否有这么一个x,使得mask_x^mask_y = new_ans。一旦找到,再判断 x ≤ 2 y x \le 2y x2y即可。具体方法见代码。
最后返回答案ans。时间复杂度 O ( n log ⁡ n + n log ⁡ U ) O(n\log n+n\log U) O(nlogn+nlogU),U为最大的bit位数。
代码:

class Solution {
public:
    int get_bit(int x){
        int k = 0;
        while(x){
            k++;
            x >>= 1;
        }
        return k;
    }
    int maximumStrongPairXor(vector<int>& nums) {
        //x <= y <= 2 * x, y = nums[i], [x,2x]
        //根据比特位来枚举,判断每个比特位是否能够为1
        //只要有一个数字满足上述条件,则当前位置可以为1
        sort(nums.begin(), nums.end());
        int ans = 0, mask = 0;
        int high_bit = 0;
        for(auto x: nums)high_bit = max(high_bit, get_bit(x));
        unordered_map<int, int>mp;
        for(int i = high_bit; i >= 0; i--){//判断每一位的最大值,即是1还是0
            mp.clear();
            mask |= 1 << i;//掩码用于将i位之后的全部置0
            int new_ans = ans | (1 << i);//新的ans,就是将当前位置置1,后面判断是否可行
            for(int y: nums){
                int mask_y = y & mask; //低于i的比特位都置为0
                auto it = mp.find(new_ans ^ mask_y); //另一个数是否存在,找的是该数掩码是否存在
                if(it != mp.end() && it -> second * 2 >= y){
                    ans = new_ans;
                    break;
                }
                mp[mask_y] = y;//数的掩码为key,该数的值为value
            }
        }
        return ans;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

a碟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值