Leetcode 第 382 场周赛题解
Leetcode 第 382 场周赛题解
题目1:3019. 按键变更的次数
思路
双指针。
给你一个下标从 0 开始的字符串 s ,该字符串由用户输入。按键变更的定义是:使用与上次使用的按键不同的键。例如 s = “ab” 表示按键变更一次,而 s = “bBBb” 不存在按键变更。
返回用户输入过程中按键变更的次数。
注意:shift 或 caps lock 等修饰键不计入按键变更,也就是说,如果用户先输入字母 ‘a’ 然后输入字母 ‘A’ ,不算作按键变更。
使用双指针指向字符串中两个相邻的字符 s[i] 和 s[j](0<=i<j<s.length(),j=i+1),将它们先转为小写,再判断是否相等。若不相等,则计入按键变更;否则,不计入。一直遍历、判断到字符串末尾。
最后返回按键变更计数。
代码
/*
* @lc app=leetcode.cn id=100215 lang=cpp
*
* [100215] 按键变更的次数
*/
// @lc code=start
class Solution
{
public:
int countKeyChanges(string s)
{
// 特判
if (s.empty())
return 0;
int n = s.length();
int count = 0;
for (int i = 0, j = 1; i < n && j < n; i++, j++)
if (tolower(s[i]) != tolower(s[j]))
count++;
return count;
}
};
// @lc code=end
复杂度分析
时间复杂度:O(n),其中 n 是字符串 s 的长度。
空间复杂度:O(1)。
题目2:3020. 子集中元素的最大数量
思路
用一个哈希表统计数组 nums 中的元素及其出现次数。
暴力枚举数组中的数,作为 x,然后不断看 x2,x4,⋯ 在数组中的个数。直到个数不足 2 个为止,退出循环。
注意模式的正中间的数字只取一个。如果最后 x 有一个,那么个数加一,否则个数减一。
注意特判 x=1 的情况。
代码
/*
* @lc app=leetcode.cn id=100206 lang=cpp
*
* [100206] 子集中元素的最大数量
*/
// @lc code=start
class Solution
{
public:
int maximumLength(vector<int> &nums)
{
// 特判
if (nums.empty())
return 0;
unordered_map<long long, int> cnt;
for (int &num : nums)
cnt[num]++;
// 特判 x=1 的情况
int ans = cnt[1] - (cnt[1] % 2 == 0);
cnt.erase(1);
for (auto &[num, _] : cnt)
{
int res = 0;
long long x;
for (x = num; cnt.contains(x) && cnt[x] > 1; x *= x)
res += 2;
if (cnt.contains(x))
res++;
else
res--;
ans = max(ans, res);
}
return ans;
}
};
// @lc code=end
复杂度分析
时间复杂度:O(nloglogU),其中 n 为数组 nums 的长度,U=max(nums)。
空间复杂度:O(n)。
题目3:3021. Alice 和 Bob 玩鲜花游戏
思路
数学。
Alice 和 Bob 在一个长满鲜花的环形草地玩一个回合制游戏。环形的草地上有一些鲜花,Alice 到 Bob 之间顺时针有 x 朵鲜花,逆时针有 y 朵鲜花。
游戏过程如下:
- Alice 先行动。
- 每一次行动中,当前玩家必须选择顺时针或者逆时针,然后在这个方向上摘一朵鲜花。
- 一次行动结束后,如果所有鲜花都被摘完了,那么当前玩家抓住对手并赢得游戏的胜利。
给你两个整数 n 和 m ,你的任务是求出满足以下条件的所有 (x, y) 对:
- 按照上述规则,Alice 必须赢得游戏。
- Alice 顺时针方向上的鲜花数目 x 必须在区间 [1,n] 之间。
- Alice 逆时针方向上的鲜花数目 y 必须在区间 [1,m] 之间。
请你返回满足题目描述的数对 (x, y) 的数目。
显然,因为是 Alice 先行动,当 x+y 为奇数时,Alice 会摘完最后一朵鲜花,抓住对手并赢得游戏的胜利。
x+y 为奇数有两种情况:
- x 为奇数,y 为偶数
- x 为偶数,y 为奇数
在 [1,n] 区间内,有 n/2 个偶数,(n+1)/2 个奇数。
所以最终答案为 (n / 2) * ((m + 1) / 2) + ((n + 1) / 2) * (m / 2)。
更进一步,把这道题想象成一个网格。
枚举一下 x 和 y 的奇偶性,得出答案为 n*m/2。
代码
/*
* @lc app=leetcode.cn id=3021 lang=cpp
*
* [3021] Alice 和 Bob 玩鲜花游戏
*/
// @lc code=start
// 数学
class Solution
{
public:
long long flowerGame(int n, int m)
{
return (long long)n * m / 2;
}
};
// Time Limit Exceeded
// class Solution
// {
// public:
// long long flowerGame(int n, int m)
// {
// long long count = 0;
// for (int x = 1; x <= n; x++)
// for (int y = 1; y <= m; y++)
// if ((x + y) ^ 0x1)
// count++;
// return count / 2;
// }
// };
// class Solution
// {
// public:
// long long flowerGame(int n, int m)
// {
// return (long long)(n / 2) * ((m + 1) / 2) +
// (long long)((n + 1) / 2) * (m / 2);
// }
// };
// @lc code=end
复杂度分析
时间复杂度:O(1)。
空间复杂度:O(1)。
题目4:3022. 给定操作次数内使剩余元素的或值最小
思路
位运算 + 试填法。
代码
/*
* @lc app=leetcode.cn id=3022 lang=cpp
*
* [3022] 给定操作次数内使剩余元素的或值最小
*/
// @lc code=start
class Solution
{
public:
int minOrAfterOperations(vector<int> &nums, int k)
{
int ans = 0, mask = 0;
// 从高位到低位一次枚举
for (int b = 29; b >= 0; b--)
{
mask |= 1 << b;
int cnt = 0;
// -1 在二进制下全为 1,所以可以省去特判每一段第一个数
int and_res = -1;
for (int &x : nums)
{
and_res &= x & mask;
if (and_res)
{
// 合并 x,操作次数加一
cnt++;
}
else
{
// 准备合并下一段,省去特判每一段的第一个数
and_res = -1;
}
}
if (cnt > k)
{
ans |= 1 << b; // 答案的这个比特位必须是 1
// 后面不考虑这个比特位,这一位一定为 1,消除这一位的影响
mask ^= 1 << b;
}
}
return ans;
}
};
// @lc code=end
复杂度分析
时间复杂度:O(nlogU),其中 n 为数组 nums 的长度,U=max(nums)。
空间复杂度:O(1)。