目录
100215. 按键变更的次数
原题链接
题目描述
接口描述
思路分析
AC代码
100195. Alice 和 Bob 玩鲜花游戏
原题链接
题目描述
接口描述
思路分析
AC代码
100206. 子集中元素的最大数量
原题链接
题目描述
接口描述
思路分析
AC代码
100179. 给定操作次数内使剩余元素的或值最小
原题链接
题目描述
接口描述
思路分析
AC代码
100215. 按键变更的次数
100215. 按键变更的次数
给你一个下标从 0 开始的字符串
s
,该字符串由用户输入。按键变更的定义是:使用与上次使用的按键不同的键。例如s = "ab"
表示按键变更一次,而s = "bBBb"
不存在按键变更。返回用户输入过程中按键变更的次数。
注意:
shift
或caps lock
等修饰键不计入按键变更,也就是说,如果用户先输入字母'a'
然后输入字母'A'
,不算作按键变更。
class Solution {
public:
int countKeyChanges(string s) {
}
};
一次遍历,直接模拟即可
class Solution {
public:
int countKeyChanges(string s) {
int ret = 0;
char pre = s[0];
for(auto x : s)
ret += tolower(x) != tolower(pre) , pre = x;
return ret;
}
};
100195. Alice 和 Bob 玩鲜花游戏
Alice 和 Bob 在一个长满鲜花的环形草地玩一个回合制游戏。环形的草地上有一些鲜花,Alice 到 Bob 之间顺时针有
x
朵鲜花,逆时针有y
朵鲜花。游戏过程如下:
- Alice 先行动。
- 每一次行动中,当前玩家必须选择顺时针或者逆时针,然后在这个方向上摘一朵鲜花。
- 一次行动结束后,如果所有鲜花都被摘完了,那么 当前 玩家抓住对手并赢得游戏的胜利。
给你两个整数
n
和m
,你的任务是求出满足以下条件的所有(x, y)
对:
- 按照上述规则,Alice 必须赢得游戏。
- Alice 顺时针方向上的鲜花数目
x
必须在区间[1,n]
之间。- Alice 逆时针方向上的鲜花数目
y
必须在区间[1,m]
之间。请你返回满足题目描述的数对
(x, y)
的数目。
class Solution {
public:
long long flowerGame(int n, int m) {
}
};
只有鲜花总数目为奇数时,Alice才能赢,(n / 2) * (m - m / 2) + (n - n / 2) * (m / 2)即为所有x * y为奇数的数对数目,没什么好说的
class Solution {
public:
long long flowerGame(long long n, long long m) {
return (n / 2) * (m - m / 2) + (n - n / 2) * (m / 2);
}
};
100206. 子集中元素的最大数量
给你一个 正整数 数组
nums
。你需要从数组中选出一个满足下述条件的
子集
:
- 你可以将选中的元素放置在一个下标从 0 开始的数组中,并使其遵循以下模式:
[x, x2, x4, ..., xk/2, xk, xk/2, ..., x4, x2, x]
(注意,k
可以是任何 非负 的 2 的幂)。例如,[2, 4, 16, 4, 2]
和[3, 9, 3]
都符合这一模式,而[2, 4, 8, 4, 2]
则不符合。返回满足这些条件的子集中,元素数量的 最大值 。
class Solution {
public:
int maximumLength(vector& nums) {
}
};
把原数组从小到大排序,哈希表存每个数字出现次数,然后遍历排序后的数组,枚举每个数字作为开头的目标子集,不断平方直到哈希表中这个值出现次数小于2,该数字作为开头的子集数目即为平方次数乘2-1
也是模拟题
时间复杂度:O(nlogn)
class Solution {
public:
int maximumLength(vector& nums) {
unordered_map mp;
for(auto x : nums) mp[x]++;
sort(nums.begin() , nums.end());
long long ret = max(1 , mp[1] & 1 ? mp[1] : mp[1] - 1);
for(auto x : nums)
{
if(x == 1) continue;
long long y = x , s = 0;
while(mp.count(y))
{
if(mp[y])
s += 2;
if(mp[y] < 2)
break;
y *= y;
}
ret = max(ret , s - 1);
}
return ret;
}
};
100179. 给定操作次数内使剩余元素的或值最小
给你一个下标从 0 开始的整数数组
nums
和一个整数k
。一次操作中,你可以选择
nums
中满足0 <= i < nums.length - 1
的一个下标i
,并将nums[i]
和nums[i + 1]
替换为数字nums[i] & nums[i + 1]
,其中&
表示按位AND
操作。请你返回 至多
k
次操作以内,使nums
中所有剩余元素按位OR
结果的 最小值 。
class Solution {
public:
int minOrAfterOperations(vector& nums, int k) {
}
};
显然是一道考察位运算tip的题,尝试从位运算角度去思考
对于题目给的相与替换操作,可以使得最终相或的值相较于不操作原数组相或的值的某些二进制位由1变0
我们设原数组相或值为num,最优操作后相或值为ans,那么ans在二进制下为num的子集,因为我们的操作不能使得1的位增加,只能使得1的位减少
所以我们若干次操作后的或值最多只有log(num)种
那对于每一种我们如果能通过遍历一次数组就能确定其是否存在,时间复杂度就为O(nlogn),这题就能过了
所以有了思路,从高位枚举,设当前能被干掉的所有位为1组成的数字为ret,那么我们现在如果要判断第i位是否能被干掉,就相当于判断ret | (1 << i)是否能被干掉(因为为了最终的值尽可能小,我们优先干掉位高的)
类似于最长递增子数组的思想,我们一次遍历数组,只要当前的相与值和ret | (1 << i)相与为0,我们就计数加一,然后相与值再复位为二进制全1
最终我们就得到了相与值和ret | (1 << i)相与为0的子数组的最大数目,那么我们干掉ret | (1 << i)的操作次数就是len(nums) - 子数组数目,维护我们能够干掉的数字位即可
最终返回没有被干掉的数字位组成的数字就是答案
时间复杂度O(nlogU)
class Solution
{
public:
int minOrAfterOperations(vector &nums, int k)
{
int ret = 0 , n = nums.size();
for(int i = 29 ; i >= 0 ; i--)
{
int cur = ret | (1 << i) , s = -1;
int cnt = n;
for(auto x : nums)
{
s &= cur & x;
if(!s) cnt-- , s = -1;
}
if(cnt <= k) ret = cur;
}
return (1 << 30) - 1 - ret;
}
};