不定长滑动窗口篇--罗姆的刷题记录

题单来自灵神,这里针对我的刷题过程做一个记录,本系列的目的是记录心得+做题记录的统计

2.1 越短越合法/求最长/最大

3. 无重复字符的最长子串

题意: 给一个字符串s,找出不含重复字符的最长子串长度。

code
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int n = s.size();
        unordered_map<char, int> cnt;
        int l = 0, r = 0, res = 0;
        for (r = 0; r < n; r++) {
            cnt[s[r]]++;
            if (cnt[s[r]] < 2) {
                res = max(res, r - l + 1);
            } else {
                while (cnt[s[r]] > 1)
                    cnt[s[l++]]--;
            }
        }
        return res;
    }
};
写的有点麻烦,可以调整下顺序,让代码更简单些,先去掉重复字符,在计算答案
simple code
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int n = s.size();
        unordered_map<char, int> cnt;
        int l = 0, r = 0, res = 0;
        for (r = 0; r < n; r++) {
            cnt[s[r]]++;
            while (cnt[s[r]] > 1)
                cnt[s[l++]]--;
            res = max(res, r - l + 1);
        }
        return res;
    }
};

3090. 每个字符最多出现两次的最长子字符串 1329

题意:给一个字符串s, 找出满足每个字符最多出现两次的最长子字符串,返回长度。
分析: 和第一题一样。

code
class Solution {
public:
    int maximumLengthSubstring(string s) {
        int n = s.size(), res = 0;
        unordered_map<char, int> cnt;
        int l = 0, r = 0;
        for (int r = 0; r < n; r++) {
            cnt[s[r]]++;
            while (cnt[s[r]] > 2)
                cnt[s[l++]]--;
            res = max(res, r - l + 1);
        }
        return res;
    }
};

1493. 删掉一个元素以后全为 1 的最长子数组 1423

题意:给一个二进制数组nums, 要删一个元素。删掉元素的结果数组中,返回最长的且 只包含1 的非空子数组长度。 不存在返回0
分析:删一个后只包含1, 也就是说窗口内最多一个0, 求最长窗口大小。(返回窗口大小 - 1)

code
class Solution {
public:
    int longestSubarray(vector<int>& nums) {
        int res = 0, n = nums.size();
        int l = 0, r = 0, zeroCnt = 0;
        for (int r = 0; r < n; r++) {
            if (nums[r] == 0)
                zeroCnt++;
            while (zeroCnt > 1) {
                if (nums[l] == 0)
                    zeroCnt--;
                l++;
            }
            res = max(res, r - l + 1);
        }
        return res-1;
    }
};

3634. 使数组平衡的最少移除数目 1453

题意:给一个正数组nums, 和一个整数k. 如果数组最大值至多 是最小值的k 倍, 则该数组被称为是平衡的。
可以从nums 中移除任意数量的元素,但是不能空数组。 为了让数组平衡, 需移除的最小数量。
分析: 数组排序,要么删最大,要么删最小。等价于在数组中找一个中间的区间保留,要求该区间是符合条件的。

code
class Solution {
public:
    int minRemoval(vector<int>& nums, int k) {
        int n = nums.size(), res = 1;
        sort(nums.begin(), nums.end());
        int l = 0, r = 0, cur = nums[0];
        for (int r = 1; r < n; r++) {
            while ((long long)nums[r] > (long long)cur * k) {
                cur = nums[++l];
            }
            res = max(res, r - l + 1);
        }
        return n - res;
    }
};

1208. 尽可能使字符串相等 1497

题意:两个长度相同的串 s ,t。将 s 中第 i 个字符编导t中第 i个字符,需要 |s[i]-t[i]|的开销。用于变更最大预算是 maxCost。 如果可以把s的子字符串转化成t的子字符串,返回可以转化的最大长度。 没有转化为0 。
分析:计算cost数组,然后找到最长的 满足 预算内的 区间长度。

code
class Solution {
public:
    int equalSubstring(string s, string t, int maxCost) {
        int n = s.size(), res = 0;
        vector<int> cost(n, 0);
        for (int i = 0; i < n; i++)
            cost[i] = abs(s[i] - t[i]);
        int l = 0, r = 0;
        for (int r = 0; r < n; r++) {
            maxCost -= cost[r];
            while (maxCost < 0)
                maxCost += cost[l++];
            res = max(res, r - l + 1);
        }
        return res;
    }
};

904. 水果成篮 1516

题意:fruits数组表示果树, 一排果树的种类。 采摘水果,要求:

  1. 只有两个篮子,每个篮子只能装一种水果,装的数量没有限制。
  2. 可以选任意一棵开始采摘,必须从每棵树恰好摘一个,应该符合篮子中水果类型。每摘一次向右移动一次。不符合就停止。 求最大水果数目
    分析: 模版题。
code
class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        int n = fruits.size();
        int res = 0, diffCnt = 0;
        unordered_map<int, int> cnt;
        for (int l = 0, r = 0; r < n; r++) {
            cnt[fruits[r]]++;
            if (cnt[fruits[r]] == 1)
                diffCnt++;
            while (diffCnt > 2) {
                cnt[fruits[l]]--;
                if (cnt[fruits[l]] == 0)
                    diffCnt--;
                l++;
            }
            res = max(res, r - l + 1);
        }
        return res;
    }
};

1695. 删除子数组的最大得分 1529

题意:给一个整数数组nums,要删除一个含有 若干 不同元素 的子数组,删除的得分就是子数组元素和,返回只删一个子数组的最大得分
分析: 模版题

code
class Solution {
public:
    int maximumUniqueSubarray(vector<int>& nums) {
        int n = nums.size(), res = 0, sum = 0;
        unordered_map<int, int> cnt;
        for (int l = 0, r = 0; r < n; r++) {
            cnt[nums[r]]++;
            sum += nums[r];
            while (cnt[nums[r]] > 1) {
                cnt[nums[l]]--;
                sum -= nums[l];
                l++;
            }
            res = max(res,sum);
        }
        return res;
    }
};

2958. 最多 K 个重复元素的最长子数组 1535

题意:给一个整数数组nums, 和一个整数k。
一个元素x 在数组中的频率指出现次数。求频率小于k的最长子数组。

code
class Solution {
public:
    int maxSubarrayLength(vector<int>& nums, int k) {
        int n = nums.size();
        unordered_map<int, int> cnt;
        int res = 0;
        for (int l = 0, r = 0; r < n; r++) {
            cnt[nums[r]]++;
            while (cnt[nums[r]] > k) {
                cnt[nums[l++]]--;
            }
            res = max(res, r - l + 1);
        }
        return res;
    }
};

2024. 考试的最大困扰度 1643

题意:老师出n道题, 每道答案 'T' 或者 'F' ,想增加不确定性,最大化连续相同结果的题数。 给一个字符串answerkey, 表示问题正确答案。还给一个k,每次可以将答案改成T 或者F, 返回不超过k次操作情况下, 最大连续T 或F数目。

分析: 一开始想的是T/F分情况两次滑窗,其实等价于 窗口内T/F都超过k 不行,其中一个不超过即可。 代码中判断TF的部分进一步可以根据ASCII码奇偶不同进一步优化。

code
class Solution {
public:
    int maxConsecutiveAnswers(string answerKey, int k) {
        int n = answerKey.size();
        int res = 0;
        vector<int> cnt(2, 0);
        for (int l = 0, r = 0; r < n; r++) {
            if (answerKey[r] == 'T')
                cnt[0]++;
            else
                cnt[1]++;
            while (cnt[0] > k && cnt[1] > k) {
                if (answerKey[l] == 'T')
                    cnt[0]--;
                else
                    cnt[1]--;
                l++;
            }
            res = max(res, r - l + 1);
        }
        return res;
    }
};

1004. 最大连续 1 的个数 III 1656

题意: 给一个二进制数组nums 和一个整数k。假设最多可以反转k个0,返回执行操作后数组中连续1的最大个数。
分析: 找最长区间,里面0个数小于等于k

code
class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int n = nums.size(), res = 0;
        for (int l = 0, r = 0; r < n; r++) {
            if (nums[r] == 0)
                k--;
            while (k < 0) {
                if (nums[l] == 0)
                    k++;
                l++;
            }
            res = max(res, r - l + 1);
        }
        return res;
    }
};

2.1.2 进阶

上面的基础做起来还是比较快的。感觉比定长的后面几题做起来舒服多了。

2730. 找到最长的半重复子字符串 非暴力做法

题意:给一个字符串s, 只包含0-9字符串。 如果一个字符串t中至多有一对相邻字符是相等的,称这个串t是半重复的。例如0010, 002020. 2002, 54944返回s中最长半重复的子字符串长度。

code
class Solution {
public:
    int longestSemiRepetitiveSubstring(string s) {
        int n = s.size(), res = 1;
        int same = 0;
        for (int l = 0, r = 0; r < n; r++) {
            if (r > 0 && s[r] == s[r - 1])
                same++;
            if (same > 1) {
                l++;
                while (s[l] != s[l - 1]) {
                    l++;
                }
                same = 1;
            }
            res = max(res, r - l + 1);
        }
        return res;
    }
};

2779. 数组的最大美丽值 1638

题意:给一个下标从0开始的整数数组nums, 非负整数k。在一步操作中可以执行下述指令:

  1. 选一个之前没选过的下标i。
  2. 把nums[i]换成 nums[i]-k , nums[i] +k 的任一整数。
    数组美丽🈯值定义为数组中相等元素组成的最长子序列长度,

对数组执行上述操作任意次后,返回最大美丽值。
每个下标只能执行一次。

分析: 每个数字有一个变化区间, 求所有区间交集最多的那个数字是多少。哦哦先排序, 然后滑窗。

code
class Solution {
public:
    int maximumBeauty(vector<int>& nums, int k) {
        int n = nums.size(), res = 0;
        sort(nums.begin(), nums.end());
        for (int l = 0, r = 0; r < n; r++) {
            while(nums[r] - nums[l] > k * 2){
                l++;
            }
            res = max(res, r-l+1);
        }
        return res;
    }
};

1658. 将 x 减到 0 的最小操作数 1817

题意:给一个nums 和k , 每次可以移除最左边或最右边,然后从x减去。 如果可以将x恰好减到0, 返回最小操作数。
注意数据边界。

code
class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int n = nums.size(), res = -1, sum = 0;
        int total = reduce(nums.begin(), nums.end()); // total - x
        if (total - x < 0)
            return -1;
        for (int l = 0, r = 0; r < n; r++) {
            sum += nums[r];
            while (sum > total - x) {
                sum -= nums[l++];
            }
            if (sum + x == total)
                res = max(res, r - l + 1);
        }
        cout << res << endl;
        if (res == -1)
            return -1;
        return n - res;
    }
};

1838. 最高频元素的频数 1876

题意:元素的频数是元素在数组中出现次数,给一个nums 和一个k , 每次可以次选择nums一个下标,把对应元素值加1. 执行最多k次操作后,返回最高频元素的最大可能频数。
分析: 排序后维护一个窗口,窗口中元素都变成最右端的target 元素,

code
class Solution {
public:
    int maxFrequency(vector<int>& nums, int k) {
        int n = nums.size();
        sort(nums.begin(), nums.end());

        long long sum = 0;
        int  res = 0;
        for (int l = 0, r = 0; r < n; r++) {
            sum += nums[r];
            while (1LL* nums[r] * (r - l + 1) > sum + k) {
                sum -= nums[l++];
            }
            res = max(res, r - l + 1);
        }
        return res;
    }
};

2516. 每种字符至少取 K 个 1948

题意:给一个由字符 'a','b','c' 组成的字符串s 和一个非负整数k 。每分钟可以选择取走s最左侧还是最右侧的字符。 必须取走每种字符至少k个,返回需要的最少分钟数。
如果无法做到返回-1.

code
class Solution {
public:
    int takeCharacters(string s, int k) {
        int n = s.size();
        vector<int> cnt(3, 0);
        for (int i = 0; i < n; i++)
            cnt[s[i] - 'a']++;
        for (int i = 0; i < 3; i++) {
            if (cnt[i] < k)
                return -1;
            cnt[i] -= k;
        }
    
        int sum = 0, res = 0;
        for (int l = 0, r = 0; r < n; r++) {
            cnt[s[r] - 'a']--;
            while (cnt[s[r] - 'a'] < 0) {
                cnt[s[l++] - 'a']++;
            }
            res = max(res, r - l + 1);
        }
        return n - res;
    }
};

2831. 找出最长等值子数组 1976

题意:给一个nums 和整数 k ,如果所有元素相等,认为是一个等值子数组。 空数组是等值子数组。从nums 中删除最多k个元素后, 返回可能的最长等值子数组的长度。
分析:想了一会没见过这类题,看了灵神题解妙啊。 分组+滑窗。 尤其是 优化了 pos 数组存储内容 简化最终判断条件比较优雅。

code
class Solution {
public:
    int longestEqualSubarray(vector<int>& nums, int k) {
        int n = nums.size();
        int res = 0;
        vector<vector<int>> pos_list(n + 1);
        for (int i = 0; i < n; i++) {
            int x = nums[i];
            pos_list[x].push_back(i - pos_list[x].size());
        }
        for (auto& pos : pos_list) {
            int l = 0;
            for (int r = 0; r < pos.size(); r++) {
                while (pos[r] - pos[l] > k) {
                    l++;
                }
                res = max(res, r - l + 1);
            }
        }

        return res;
    }
};
posted on 2026-05-31 17:46  Eura42  阅读(4)  评论(0)    收藏  举报