Day58 | 灵神 | 滑动窗口:长度最小的子数组&&乘积小于K的子数组&&无重复字符的最长子串

Day58 | 灵神 | 滑动窗口:长度最小的子数组&&乘积小于K的子数组&&无重复字符的最长子串

209.长度最小的子数组

209. 长度最小的子数组 - 力扣(LeetCode)

思路:

思考的关键点:固定右指针,移动左指针,而我们要做的就是找到左指针移动的条件,即在什么情况下移动左指针,而这个条件一般都在题目中。

在本题中,我们固定右端点

image-20250310182605966

以固定4,我们目前左指针l=0,指向的是2,右指针为4,指向4

我们发现这个子数组的和是大于target7的,满足了子数组和大于target的条件,子数组大小是5,那这个时候我们就可以通过移动左指针来缩小子数组的大小。我们移动左指针,l++,发现和是10,仍旧大于target,那说明我们可以继续往左移动指针来缩小子数组的大小,一直到l=2,指向1时,我们找到了子数组和仍大于等于7的最小的子数组,因为再减一位就不大于7了,就不满足条件了,所以我们更新答案,就是右指针减去左指针再加1就是这个子数组的长度,即4-2+1=3。到这里我们就找到了以4为右端点的满足条件的最小的子数组的长度为3。

那么只要从头遍历右端点,每个端点都重复这个过程,在这之间记录最小值,那么就会找到我们想要的答案。

完整代码:

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int res=INT_MAX;//最后结果
        int sum=0;//记录数组和
        int l=0;//左指针
        //i是右指针,右端点
        for(int i=0;i<nums.size();i++)
        {
            sum+=nums[i];
            //满足条件 子数组和大于target
            while(sum>=target)
            {
                int temp=i-l+1;
                //更新答案
                res=min(res,temp);
                sum-=nums[l];
                //更新左指针
                l++;
            }
        }
        //res还是初始值,说明整个数组加起来都不到target,返回0即可
        if(res==INT_MAX)
            return 0;
        return res;
    }
};

713.乘积小于K的子数组

713. 乘积小于 K 的子数组 - 力扣(LeetCode)

思路:

和上一题思路一样,只是这道题更新答案变为在while循环外。因为上一题在while循环内是,是满足子数组和大于target这一条件的,所以在while内更新答案。而本题sum<k即在while外才是满足条件的情况,所以在while循环外更新答案。

本题的关键点,也就是左指针移动条件是子数组乘积大于K时进行移动。

另一个关键点是子数组的数量如何计算。

假设当前满足条件的子数组为[5,2,6],那么以6为右端点(即必须包括6)有几个满足条件的数组?

[5,2,6],[2,6],[6]这三个。而我们不需要管[5],[5,2]这类的子数组,因为这类的数组在以5或者2为右端点时已经计算过一次了。

推广一下,[l,r]这个区间的子数组乘积小于k,那么[l+1,r],…[r,r]都是小于k的,那么数量就是

r-l+1个子数组满足条件

完整代码:

class Solution {
public:
    int numSubarrayProductLessThanK(vector<int>& nums, int k) {
        if(k<=1)
            return 0;
        int res=0;
        int l=0;
        int sum=1;
        for(int i=0;i<nums.size();i++)
        {
            sum*=nums[i];
            //更新l使得子数组满足条件
            while(sum>=k)
            {
                //res+=(i-l+1);
                sum/=nums[l];
                l++;
            }
            //结果更新是在满足题目条件时进行更新,上一道题在while内更新是因为在while内是满足条件的时候
            //这道题是while外才是符合条件的时候,所以在while外更新结果
            res+=(i-l+1);
        }
        return res;
    }
};

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

3. 无重复字符的最长子串 - 力扣(LeetCode)

思路:

和上面两道题思路相同,固定右端点,遍历右端点。只要碰到重复的字符,那么左指针就一直往右移动,直到没有重复的字符为止。

记录字符的方式:哈希表,key为字符,value为字数个数,只要不为1就说明有重复,那就左指针一直往右移动,直到没有重复的字符。

在遍历过程中记录子串的长度,碰到长度更大的子串就更新结果。

完整代码:

注释部分是笔者原来的错误,没有找到正确的记录字符的方式。仅作为一个记录,不必在意

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int res=0;
        unordered_map<char,int> p;
        int l=0;
        for(int i=0;i<s.size();i++)
        {
            /*if(p.find(s[i])==p.end())
            {
                p.insert({s[i],i});
                res=max(res,i-l);
            }
            else
            {
                res=max(res,i-l);
                int temp=p[s[i]]+1;
                for(int j=l;j<=p[s[i]];j++)
                    p.erase(s[j]);
                l=temp;
                p.insert({s[i],i});
            }*/
            
            //记录字符
            p[s[i]]++;
            //移动左指针
            while(p[s[i]]!=1)
            {
                p[s[l]]--;
                l++;
            }
            //更新答案
            res=max(res,i-l+1);
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值