滑动窗口算法-day11(不定长选做)
1.每种字符至少取 K 个
题目
解析
- 要求取两边的元素,且三种元素数量不能小于 K;
- 反向思维转化为:先计算每个元素个数到 cnt,构建窗口,依次消减 cnt,若新进队元素 x 使得 cnt[x] < K,则移动窗口;
- 注意点:若本身就有元素总数小于 K,直接返回 -1;
代码
class Solution {
public:
int takeCharacters(string s, int k) {
// 时间复杂度:O(n)
// 空间复杂度:O(1)
int n = s.size();
int ans = 0;
unordered_map<char,int> cnt;
for(char c : s) cnt[c] ++;
// 如果任何字符的数量小于 k,直接返回 -1
if (cnt['a'] < k || cnt['b'] < k || cnt['c'] < k) {
return -1;
}
int l = 0;
for(int r = 0;r < n;r ++) {
cnt[s[r]] --;
while(cnt[s[r]] < k){
cnt[s[l]] ++;
l ++;
}
ans = max(ans,r - l + 1);
}
return n - ans;
}
};
2.数组的最大美丽值
题目
解析
- 由于答案记录最终数组中最多相等的数,所以与数组顺序无关,直接排序;
- 将每个数字 x 想象成一个数值范围,例如 (x - k, x + k),(y - k, y + k);
- 若要两个数字可以转换,则需要满足 x + k >= y - k,即 y - x <= 2 * k;
- 遍历数组,若两头数字不满足条件转换,则令 L++;满足则更新答案;
代码
class Solution {
public:
int maximumBeauty(vector<int>& nums, int k) {
// 时间复杂度:O(nlogn)
// 空间复杂度:O(1)
int n = nums.size();
int ans = 0;
sort(nums.begin(),nums.end());// 排序不影响结果
int l = 0;
for(int r = 0;r < n;r ++){
// 若右边界元素和左边界元素取值范围无交集,则移动窗口
while(nums[r] - nums[l] > 2 * k){
l ++;
}
ans = max(ans,r - l + 1);
}
return ans;
}
};
3.最高频元素的频数
题目
解析
- 题目要求最高频元素的频数,与数组顺序无关,直接排序;
- 核心思想:计算窗口每个元素与右边界元素的距离和;
- 一开始我的思路是加上每两个元素之间的差值,使其小于 k,但其实不是这样算;
- 计算公式为:(nums[r] - nums[r - 1]) * (r - l);
- 当距离和超过 K 时,就要移动窗口,没超过就更新答案最大值;
公式推导:
画出来的面积就是窗口每个元素与右边界元素的距离和;nums[r] - nums[r - 1] 是矩形高,r - l 是矩形宽;
代码
class Solution {
public:
int maxFrequency(vector<int>& nums, int k) {
// 时间复杂度:O(nlogn)
// 空间复杂度:O(1)
int n = nums.size();
int ans = 1;
long long sum = 0;
sort(nums.begin(),nums.end());
int l = 0;
for(int r = 1;r < n;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;
}
};