寒假刷题Day7

一、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.同样的正难则反:与其维护窗口内的字母个数,不如直接维护窗口外的字母个数;

  1. 一开始,假设我们取走了所有的字母。或者说,初始窗口是空的,窗口外的字母个数就是 s 的每个字母的出现次数。

右端点字母进入窗口后,该字母取走的个数减一。

如果减一后,窗口外该字母的个数小于 k,说明子串太长了,或者取走的字母个数太少了,那么就不断右移左端点,把左端点字母移出窗口,相当于我们取走移出窗口的字母,直到该字母个数等于 k,退出内层循环。

内层循环结束后,用窗口长度 right−left+1 更新子串长度的最大值。

最后,原问题的答案为 n 减去子串长度的最大值。

特别地,如果 s 中某个字母的个数不足 k,那么无法满足题目要求,返回 −1。

相关推荐
廖显东-ShirDon 讲编程1 分钟前
《零基础Go语言算法实战》【题目 4-6】随机选择单链表的一个节点并返回
算法·程序员·go语言·web编程·go web
廖显东-ShirDon 讲编程3 分钟前
《零基础Go语言算法实战》【题目 2-20】通过泛型比较大小
算法·程序员·go语言·web编程·go web
王景程10 分钟前
Java冒泡排序算法之:变种版
java·数据结构·算法
AQin101214 分钟前
【伪随机数】关于排序算法自测如何生成随机数而引发的……
算法·random·随机数·伪随机数
睡觉待开机26 分钟前
算法-贪心(T1~T3)
算法·贪心算法
迂幵myself1 小时前
13-1类与对象
开发语言·c++·算法
董董灿是个攻城狮2 小时前
017:推理框架为什么可以加速AI推理?
算法
zc.ovo2 小时前
寒假康复训练2 edu111(A-C)
c语言·数据结构·算法
雾月552 小时前
LeetCode1170 比较字符串最小字母出现频次
数据结构·算法
液态不合群3 小时前
G1原理—G1的GC日志分析解读
java·jvm·算法