一、1658. 将 x 减到 0 的最小操作数
给你一个整数数组 nums
和一个整数 x
。每一次操作时,你应当移除数组 nums
最左边或最右边的元素,然后从 x
中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。
如果可以将 x
恰好 减到 0
,返回最小操作数 ;否则,返回 -1
。
代码:
cpp
class Solution {
public:
int minOperations(vector<int>& nums, int x) {
int target = accumulate(nums.begin(), nums.end(), 0) - x;
if (target < 0) return -1; // 全部移除也无法满足要求
// 减去滑动串口内值后剩余元素总和为x
int l= 0;
int sum = 0, len = nums.size();
int ans = -1;
for(int r = 0; r < len; ++r){
sum += nums[r];
while(sum > target){
sum -= nums[l++];
}
if(sum == target){
ans = max(ans, r - l + 1);
}
}
return ans < 0 ? -1 : len - ans;
}
};
思路:
1.逆向思维,将计算两端元素总和为x 化为计算中间数组元素总和减去滑动窗口内总和为 x ;
2.考虑返回值为-1的情况有整个数组加起来也没x大,或者找到满足条件的子数组(ans == -1)
二、1838. 最高频元素的频数
元素的 频数 是该元素在一个数组中出现的次数。
给你一个整数数组 nums
和一个整数 k
。在一步操作中,你可以选择 nums
的一个下标,并将该下标对应元素的值增加 1
。
执行最多 k
次操作后,返回数组中最高频元素的 最大可能频数 。
代码:
cpp
class Solution {
public:
int maxFrequency(vector<int>& nums, int k) {
sort(nums.begin(), nums.end() );
int n = nums.size();
int l = 0;
int ans = 1;
long long sum = 0;
for(int r = 1; r < nums.size(); ++r){
sum += (long long)(nums[r] - nums[r - 1]) * (r - l);
while(sum > k){
sum -= nums[r] - nums[l];
++l;
}
ans = max(ans, r - l + 1);
}
return ans;
}
};
思路:
1.排序+滑动窗口,凡是只涉及个数,不涉及下标的都可以考虑试试排序;
2.计算改变次数方法是 sum += (long long)(nums[r] - nums[r - 1]) * (r - l); 因为每次加入nums[ r ] 之后需要改变前面(r - l)个数,用(r - l)*nums[ r ]与上一个nums[ r ]的差值
三、2516. 每种字符至少取 K 个
给你一个由字符 'a'
、'b'
、'c'
组成的字符串 s
和一个非负整数 k
。每分钟,你可以选择取走 s
最左侧 还是 最右侧 的那个字符。
你必须取走每种字符 至少 k
个,返回需要的 最少 分钟数;如果无法取到,则返回 -1
。
代码:
cpp
class Solution {
public:
int takeCharacters(string s, int k) {
int cnt[3]{};
for (char c : s) {
cnt[c - 'a']++; // 一开始,把所有字母都取走
}
if (cnt[0] < k || cnt[1] < k || cnt[2] < k) {
return -1; // 字母个数不足 k
}
int mx = 0, left = 0;
for (int right = 0; right < s.length(); right++) {
char c = s[right] - 'a';
cnt[c]--; // 移入窗口,相当于不取走 c
while (cnt[c] < k) { // 窗口之外的 c 不足 k
cnt[s[left] - 'a']++; // 移出窗口,相当于取走 s[left]
left++;
}
mx = max(mx, right - left + 1);
}
return s.length() - mx;
}
};
思路:
1.同样的正难则反:与其维护窗口内的字母个数,不如直接维护窗口外的字母个数;
- 一开始,假设我们取走了所有的字母。或者说,初始窗口是空的,窗口外的字母个数就是 s 的每个字母的出现次数。
右端点字母进入窗口后,该字母取走的个数减一。
如果减一后,窗口外该字母的个数小于 k,说明子串太长了,或者取走的字母个数太少了,那么就不断右移左端点,把左端点字母移出窗口,相当于我们取走移出窗口的字母,直到该字母个数等于 k,退出内层循环。
内层循环结束后,用窗口长度 right−left+1 更新子串长度的最大值。
最后,原问题的答案为 n 减去子串长度的最大值。
特别地,如果 s 中某个字母的个数不足 k,那么无法满足题目要求,返回 −1。