LeetCode | 周赛-372 做题记录

​个人博客主页:https://shansan.xyz/
博客文章原链接:https://shansan.xyz/LeetCode-372-b1486891b31c4121bc13186532c997b5

第 372场周赛

https://leetcode.cn/contest/weekly-contest-307/

AC情况:

✅ ✅ ❌ ❌

1. 使三个字符串相等

给你三个字符串 s1s2s3。 你可以根据需要对这三个字符串执行以下操作 任意次数

在每次操作中,你可以选择其中一个长度至少为 2 的字符串 并删除其 最右位置上 的字符。

如果存在某种方法能够使这三个字符串相等,请返回使它们相等所需的 最小 操作次数;否则,返回 -1

解题思路:

根据题意,当进行任意操作使得三个字符串相等时,每个字符串左侧保持不变,因此,可以将题意转化为获取三个字符串从下标0开始的最长公共子序列长度。

复杂度: O ( n ) O(n) O(n)

AC代码:

class Solution {
public:
    int findMinimumOperations(string s1, string s2, string s3) {
        int l1 = s1.size(), l2 = s2.size(), l3 = s3.size();
        
        int cnt = 0;
        for(int i = 0; i < l1 && i < l2 && i < l3;  i ++) {
            if(s1[i] == s2[i] && s1[i] == s3[i]) cnt ++;
            else break;
        }
        
        if(cnt == 0) return -1;
        
        return l1+l2+l3 - 3*cnt; 
    }
};

2. 区分黑球与白球

桌子上有 n 个球,每个球的颜色不是黑色,就是白色。

给你一个长度为 n 、下标从 0 开始的二进制字符串 s,其中 10 分别代表黑色和白色的球。

在每一步中,你可以选择两个相邻的球并交换它们。

返回「将所有黑色球都移到右侧,所有白色球都移到左侧所需的 最小步数」。

解题思路:

根据题意分析,从右往左,依次将某一个黑色球都移到右侧时,考虑的最小移动步数其实等于右侧的白球数量。

以case:1000101为例

s[1]已经在序列最右侧,不需要移动

s[4]左侧s[5]为0,因此交换s[4]s[5]s=1000011

s[0]左侧s[1]s[2]s[3]s[4]为0,因此需要依次交换4次右移黑球,s=0000111

最小步数为5

计算时只需要逆序遍历,即可统计出每个黑色球右侧的白球数量,求出最小步数

复杂度: O ( n ) O(n) O(n)

AC代码:

class Solution {
public:
    long long minimumSteps(string s) {
        long long res = 0;
        int n = s.length(), cntZero = 0;
        
        for(int i = n-1; i >= 0; i --) {
            if(s[i] == '0') cntZero ++;
            else res += cntZero;
        }
        return res;
    }
};

3. 最大异或乘积

给你三个整数 abn ,请你返回 (a XOR x) * (b XOR x)最大值x 需要满足 0 <= x < 2^n

由于答案可能会很大,返回它对 109 + 7 取余 后的结果。

注意XOR 是按位异或操作。

解题思路:

比赛的时候思路大致正确,但是没有注意到a、b的范围会大于x

(a XOR x)(b XOR x)AB

首先采用位运算的方法,将整数a,b转化为二进制数。求成绩最大值,根据异或操作的原理,我们可以很轻易地得出假设:

  • a[i] = b[i]时,若要AB 最大,肯定希望A[i]=1,这是的x[i]可以直接确定下来
  • a[i] ≠ b[i]时,A[i]B[i],必有一一个为1,一个为0,把这些位数表示的数值相加得到的数记为R,不管怎么分配R的和是不会变的。

已知当两数之和固定,乘积最大,本题的问题也就转化为了在AB之间分配这些不同位数的1,使得AB最相近。

由于 0 <= x < 2^n, 需要考虑两种不同的情况

  • case1:a ≥ 2^nb ≥ 2^n时,对于位数i ≥ n的1,异或x之后结果不变,因此,需要将所有0-n位数范围的1分配给最小的数
  • case2:a < 2^nb < 2^n时,我们只需要把需要分配的最高位数给A,剩余位数分配给B即可

复杂度: O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))

AC代码:

class Solution {
public:
    //return 000...0001011
    vector<int> getBinaryNum(long long a) {
        vector<int> res;
        while(a) {
            res.push_back(a % 2);
            a /= 2;
        }
        while(res.size() < 50) res.push_back(0);
        reverse(res.begin(), res.end());
        return res;
    }
    
    long long getNum(vector<int> & a) {
        int n = a.size();
        long long res = 0;
        for(int i = 0; i < n; i ++)  res = res * 2 + a[i];
        return res;
    }
    
    int getCaseType(vector<int> & binaryA, vector<int> & binaryB, int n) {
        for(int i = 0; i <= 49-n; i ++) {
            if(binaryA[i] == binaryB[i]) continue;
            if(binaryA[i] < binaryB[i]) return 1;
            else return 2;
        }
        return 0;
    }
     
    int mod = 1e9+7;
    int maximumXorProduct(long long a, long long b, int n) {
        vector<int> binaryA = getBinaryNum(a), binaryB = getBinaryNum(b);
        vector<int> remains;
        
        for(int i = 49; i > 49-n; i --) {
            if(binaryA[i] == binaryB[i]) {
                binaryA[i] = 1;
                binaryB[i] = 1;
            } else {
                remains.push_back(i);
            }
        }
        reverse(remains.begin(), remains.end());
        int caseType = getCaseType(binaryA, binaryB, n);
        
        if(caseType == 0 && remains.size() != 0) {
            binaryA[remains[0]] = 1;
            binaryB[remains[0]] = 0;
            for(int i = 1; i < remains.size(); i ++) {
                binaryA[remains[i]] = 0;
                binaryB[remains[i]] = 1;
            }
        } else if (caseType == 1) {
            //A < B
            for(int i = 0; i < remains.size(); i ++) {
                binaryA[remains[i]] = 1;
                binaryB[remains[i]] = 0;
            }
        } else {
            for(int i = 0; i < remains.size(); i ++) {
                binaryB[remains[i]] = 1;
                binaryA[remains[i]] = 0;
            }
        }
        
        return ((getNum(binaryA) % mod) * (getNum(binaryB) % mod)) % mod;

    }
};

4. 找到 Alice 和 Bob 可以相遇的建筑

给你一个下标从 0 开始的正整数数组 heights ,其中 heights[i] 表示第 i 栋建筑的高度。

如果一个人在建筑 i ,且存在 i < j 的建筑 j 满足 heights[i] < heights[j] ,那么这个人可以移动到建筑 j

给你另外一个数组 queries ,其中 queries[i] = [ai, bi] 。第 i 个查询中,Alice 在建筑 ai ,Bob 在建筑 bi

请你能返回一个数组 ans ,其中 ans[i] 是第 i 个查询中,Alice 和 Bob 可以相遇的 最左边的建筑 。如果对于查询 i ,Alice **和 **Bob 不能相遇,令 ans[i]-1

解题思路:

比赛时只想到可以使用离线排序的方式,设置queries[i][0] < queries[i][1],从左向右遍历求解,但当时考虑了单调栈(错误思路),以下参考灵神最小堆的题解:

首先可以对于queries[i][0]queries[i][1]的大小关系不会影响解答,因此默认设置queries[i][0]<queries[i][1],遍历queries可以获取到Alice和Bob的坐标 a i a_i ai b i b_i bi

a i = b i a_i = b_i ai=bi 或者 h e i g h t s [ a i ] < h e i g h t s [ b i ] heights[a_i] < heights[b_i] heights[ai]<heights[bi] ,Alice可以直接调到Bob的位置, b i b_i bi 就是解答;

否则,我们记录 b i b_i bi左侧 a i a_i ai与其属于的查询编号,最后从小到大遍历建筑时,使用最小堆维护记录

  • 遍历到 h e i g h t s [ i ] heights[i] heights[i] 时,把在下标i处的「记录」全部加到最小堆中。
  • 在加到最小堆之前,我们可以回答左边所有高度小于 h e i g h t s [ i ] heights[i] heights[i] 的「记录」,其答案就是i

复杂度: O ( n + k l o g ( k ) ) O(n + klog(k)) O(n+klog(k))nheights的长度,kqureies的长度

AC代码:

class Solution {
public:
    vector<int> leftmostBuildingQueries(vector<int>& heights, vector<vector<int>>& queries) {
        vector<int> ans(queries.size(), -1);
        vector<vector<pair<int,int>>> left(heights.size());

        for(int qi = 0; qi < queries.size(); qi ++) {
            int i = queries[qi][0], j = queries[qi][1];
            //默认i < j
            if(i > j) swap(i, j);
            if(i == j || heights[i] < heights[j]) ans[qi] = j;
            else left[j].emplace_back(heights[i], qi);
        }

        priority_queue<pair<int,int>, vector<pair<int,int>>, greater<>> pq;
        for(int i = 0; i < heights.size(); i ++)  {
            //遍历建筑i,当遇到堆顶元素高度<建筑i,就弹出,这时确保堆顶在i左侧,且i为离栈顶元素最近的解
            while(!pq.empty() && pq.top().first < heights[i]) {
                ans[pq.top().second] = i;
                pq.pop();
            }
            for(auto &p: left[i]) pq.emplace(p);
        }
        return ans;
    }
};

参考链接

灵茶山艾府 https://leetcode.cn/problems/find-building-where-alice-and-bob-can-meet/description/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值