【专题二】滑动窗口(3)

📝前言说明:

  • 本专栏主要记录本人的基础算法学习以及LeetCode刷题记录,按专题划分
  • 每题主要记录:(1)本人解法 + 本人屎山代码;(2)优质解法 + 优质代码;(3)精益求精,更好的解法和独特的思想(如果有的话)
  • 文章中的理解仅为个人理解。如有错误,感谢纠错

🎬个人简介:努力学习ing
📋本专栏:C++刷题专栏
📋其他专栏:C语言入门基础python入门基础C++学习笔记Linux
🎀CSDN主页 愚润泽


2730. 找到最长的半重复子字符串

在这里插入图片描述

个人解

思路:

滑动窗口,要从暴力解法里面,利用问题的单调性,然后实现不回退操作,优化成滑动窗口。
暴力解法

  1. 步骤一:left指针指向子数组的左边,right指向子数组的右边。
  2. 步骤二:对于每个子数组,遍历一遍,判断子数组中连续出现的字符对数,判断是不是半重复子字符串。
  3. 步骤三:如果满足,就更新答案,然后right++,重复步骤二,探索更长的。如果不满足,就left++,重复步骤二。

时间复杂度:O( n 2 n^2 n2)

优化成滑动窗口(核心的要点就是变成同向双指针,不要让right回退):
left遍历一遍字符串,这个步骤肯定没办法优化。我们要优化的只能是:对于每个子字符串判断是不是半重复子字符串这一步骤。
暴力解法中,每次都是通过重新遍历子字符串来判断的。这个过程怎么优化?
因为字符串具有单调性:假设原来的子字符串满足要求,那么,只有每加入一个新字符时,才有可能导致原来的半重复子字符串不满足要求(并且这个新加入的字符,肯定要和原来字符串的最后一个字符相同)【left出窗口同理】
利用这一点,我们就无需重新遍历子字符串,无需关注子字符串前面的部分,只需关注每次可能带来变动的行为。并且将这一行为带来的连续字符对的数目的变化记录下来,从而判断。达到right不回退的效果

用时:7:00
屎山代码:

class Solution {
public:
    int longestSemiRepetitiveSubstring(string s) {
        int k = 0, ans = 0, n = s.size();
        for(int left = 0, right = 0; right < n; right++)
        {
            // 进窗口操作是 right++
            if(right && s[right - 1] == s[right]) // 维护窗口
            {
                k++;
            }

            if(k > 1) // 判断
            {
                left++; // 出窗口
                if(s[left - 1] == s[left])
                {
                    k--;
                }
            }
            ans = max(ans, right - left + 1);
        }
        return ans;
    }
};

时间复杂度:O(n)
空间复杂度:O(1)


2779. 数组的最大美丽值

在这里插入图片描述

个人解

思路:
我写题题时的思考注释:

    // 暴力解法:
    // 第 i 个位置能选择的数的范围是:[ai, bi]
    // 找出所有位置能选择的出现最多次数的数
    // 对于每个位置求出能选择的数的范围区间(记录最大值和最小值)
    // 然后对所有范围区间找重叠部分(找出重叠最多的数字 / 区间)
    // 有几个区间可以在那个位置重叠,那答案就是几

    // 暴力:
    // 先对数组排序
    // 然后遍历整个数组,对于每个数生成一个区间
    // 然后再遍历数组,判断有多少个数生成的区间能和这个数生成的区间重叠
    // 即:小数的右边界要能大于大数的左边界。
    // 记录最大值

    // 滑动窗口优化:
    // 单调性:数字越大的数的左边界越大。
    // 所以,比如[a1,a2,a3,a4,,a5]
    // 当 a1 和 a4 没有重叠部分的时候,没必要再重新计算a2 和 a3 是否有重叠部分,a1和a3有,那a2和a3肯定也有

用时:30:00
屎山代码(通过):

class Solution {
public:
    int maximumBeauty(vector<int>& nums, int k) {
        ranges::sort(nums);
        int  ans = 0, n = nums.size();
        for(int left = 0, right = 0; right < n; right++)
        {
            // right++就是进窗口
            while(nums[right] - k > nums[left] + k) // 判断
            {
                left++;
            }
            ans = max(right - left + 1, ans);
        } 
        return ans;
    }
};

时间复杂度:O(n)
空间复杂度:O(1)


优质解:

思路:别人的思路和我的也差不多,就不展示了

2302. 统计得分小于 K 的子数组数目

在这里插入图片描述

个人解

思路:这个没什么好讲的,和之前的大部分题目(比如求满足元素和<k的最长子数组等)差不多
用时:7:00
屎山代码:

class Solution {
public:
    long long countSubarrays(vector<int>& nums, long long k) {
        // 单调性:因为数组内元素都大于0,每多增加一个数字,长度和和都会增加,所以分数也肯定增加
        long long sum = 0, ans = 0;
        for(int left = 0, right = 0; right < nums.size(); right++)
        {
            sum += nums[right]; // 进窗口
            while(sum * (right - left + 1) >= k) // 判断
            {
                sum -= nums[left++]; // 出窗口
            }
            ans += right - left + 1;
        }
        return ans;
    }
};

时间复杂度:O(n)
空间复杂度:O(1)


🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愚润泽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值