个人博客主页:https://shansan.xyz/
博客文章原链接:https://shansan.xyz/LeetCode-372-b1486891b31c4121bc13186532c997b5
第 372场周赛
https://leetcode.cn/contest/weekly-contest-307/
AC情况:
✅ ✅ ❌ ❌
1. 使三个字符串相等
给你三个字符串 s1
、s2
和 s3
。 你可以根据需要对这三个字符串执行以下操作 任意次数 。
在每次操作中,你可以选择其中一个长度至少为 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
,其中 1
和 0
分别代表黑色和白色的球。
在每一步中,你可以选择两个相邻的球并交换它们。
返回「将所有黑色球都移到右侧,所有白色球都移到左侧所需的 最小步数」。
解题思路:
根据题意分析,从右往左,依次将某一个黑色球都移到右侧时,考虑的最小移动步数其实等于右侧的白球数量。
以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. 最大异或乘积
给你三个整数 a
,b
和 n
,请你返回 (a XOR x) * (b XOR x)
的 最大值 且 x
需要满足 0 <= x < 2^n
。
由于答案可能会很大,返回它对 109 + 7
取余 后的结果。
注意,XOR
是按位异或操作。
解题思路:
比赛的时候思路大致正确,但是没有注意到a、b的范围会大于x
计(a XOR x)
和 (b XOR x)
为A
和 B
首先采用位运算的方法,将整数a,b转化为二进制数。求成绩最大值,根据异或操作的原理,我们可以很轻易地得出假设:
- 当
a[i] = b[i]
时,若要A
和B
最大,肯定希望A[i]=1
,这是的x[i]可以直接确定下来 - 当
a[i] ≠ b[i]
时,A[i]
和B[i]
,必有一一个为1,一个为0,把这些位数表示的数值相加得到的数记为R,不管怎么分配R的和是不会变的。
已知当两数之和固定,乘积最大,本题的问题也就转化为了在A
和 B
之间分配这些不同位数的1,使得A
和 B
最相近。
由于 0 <= x < 2^n
, 需要考虑两种不同的情况
- case1:
a ≥ 2^n
或b ≥ 2^n
时,对于位数i ≥ n
的1,异或x之后结果不变,因此,需要将所有0-n
位数范围的1分配给最小的数 - case2:
a < 2^n
或b < 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)),n
为heights
的长度,k
为qureies
的长度
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/