专题二——滑动窗口

目录

一长度最小的子数组

二无重复字符的最长字符串 

 三最大连续1的个数Ⅲ

四将x减到0的最小操作数 

五水果成篮 

六找到字符串中所有字母异位词

七串联所有单词的子串

八最小覆盖子串


原理:定义两个指针(下标)来维护所指向的区间始终是符合题目要求,大致分为三步:

1进窗口:用一个指针来进行遍历搜索使指针区间符合要求

2更新值:该区间符合要求后记录存储数值(可以是任意区间)

3出窗口:另一个指针开始进行向后走继续查找符合要求的区间

一长度最小的子数组

oj链接:长度最小的子数组

思路:定义left与right两个下标进行区间范围的查找用sum记录left与right区间之和,从中找出最小的那一段子数组:

进窗口:right移动进行sum+=

出窗口:相加的和>target,left向后移动进行sum-=直到区间之和再次符合要求(循环)

在出窗口之前更新数据从而在多次区间中找到最小的子数组

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int ret=INT_MAX,sum=0;
        for(int left=0,right=0;right<nums.size();right++)
        {
            sum+=nums[right];
            while(sum>=target)
            {
                //left与right之间的数符合要求就记录下来
                //取最小的数
                ret=min(ret,right-left+1);
                sum-=nums[left];
                left++;
            }
        }
        //ret没有变化说明不存在有连续子数组>=target
        return ret==INT_MAX?0:ret;
    }
};

二无重复字符的最长子串 

oj链接:无重复字符的最长子串 

思路:与上面思路类似:使用滑动窗口但要用一个数组来存储字符的个数,个数>1说明此时right指向的字符是重复字符要进行出窗口的动作;

最后在更新子串的数据找到最长子串 

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        //模拟哈希表存储字符个数
        int hash[128]={0},ret=INT_MIN;
        for(int left=0,right=0;right<s.size();right++)
        {
            //进窗口
            hash[s[right]]++;
            //说明right指向的字符已经出现重复
            while(hash[s[right]]>1)
            {
                //出窗口
                hash[s[left]]--;
                left++;
            }
            //更新数据
            ret=max(ret,right-left+1);
        }
        return ret==INT_MIN? 0 : ret;
    }
};

 三最大连续1的个数Ⅲ

oj链接:最大连续1的个数 III

思路:用计数器count来记录0的个数+滑动窗口思想进行解答即可:

class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        //用count记录0出现的个数
        int count=0,ret=INT_MIN;
        for(int left=0,right=0;right<nums.size();right++)
        {
            //进窗口
            if(nums[right]==0)
            {
                count++;
            }
            //right位置的0超过规定的最大翻转0的个数
            //进行出窗口操作
            while(count>k)
            {
               
                if(nums[left]==0)
                {
                    count--;
                }
                left++;
            }
            //更新数据
            ret=max(ret,right-left+1);
        }
        return ret==INT_MIN? 0 : ret;

    }
};

四将x减到0的最小操作数 

oj链接:将 x 减到 0 的最小操作数

思路:题中要想求出最小的数组个数相加=x还规定值得在左右两端进行选择,实现起来非常困难。因此,

我们将问题转化为(滑动窗口)求最长的数组个数相加=数组之和-x

剩余的数组个数不就是题目要求的吗?

class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        //正难反易
        int sum=0,len=INT_MIN,sum1=0;
        //求出数组之和
        for(auto ch : nums)
        {
            sum+=ch;
        }
        //和减去x即是我们说要求的目标值
        int target=sum-x;
        //即不可能找到最长数组长度(都是正整数)
        if(target<0)
        {
            return -1;
        }
        //求最小操作数转化为求出最长数组长度=target
        for(int left=0,right=0;right<nums.size();right++)
        {
            sum1+=nums[right];
            while(sum1>target)
            {
                sum1-=nums[left];
                left++;
            }
            if(sum1==target)
            {
                len=max(len,right-left+1);
            }
        }
        //如果没找到返回-1,找到返回总长-len
        return len==INT_MIN?-1:nums.size()-len;
    }
};

五水果成篮 

 oj链接:水果成篮

思路:哈希表+计数器+滑动窗口

用数组模拟哈希表来存储数值,用count记录水果种类:

进窗口:right进行遍历存储数值与种类的记录

出窗口:种类超过2种,left进行向右移动直到种类在2种一下(循环)

更新结果记录left与right的水果个数,找到最多的那个即为被题答案!

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        //计数器count与数组存储值
        int count=0,ret=INT_MIN,hash[100001]={0};
        for(int left=0,right=0;right<fruits.size();right++)
        {
            if(hash[fruits[right]]==0)
            {
                count++;
            }
            hash[fruits[right]]++;
            while(count>2)
            {
                hash[fruits[left]]--;
                if(hash[fruits[left]]==0)
                {
                    count--;
                }
                left++;
            }
            ret=max(ret,right-left+1);
        }
        return ret==INT_MIN? 0:ret;
    }
};

六找到字符串中所有字母异位词

oj链接:找到字符串中所有字母异位词

思路:先用数组模拟哈希表统计p中的字符,创建计数器count来统计‘有效字符的个数’;

用滑动窗口的思想:定义left与right,right向后走的同时,也用哈希表来统计字符,当字符个数<= p字符的个数即为有效字符,count++;

让left与right的区间始终保持在len(p字符的个数)个。当count=len时这段区间就是我们要找的字母异位词!

进窗口:right走的同时统计走过的字符个数

出窗口:只要left与right之间的个数>len就进行left出窗口的操作(因为是区间是固定所以仅需判断即可)

更新数值:cout==len这段窗口就是我们的答案,记录left的下标。

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> ans;
        //count统计“有效字符的个数”即
        int hash1[128]={0},hash2[128]={0},count=0,len=p.size();
        //hash1统计p字符
        for(auto ch : p)
        {
            hash1[ch]++;
        }
        //hash2用来统计left与right之间的字符
        for(int left=0,right=0;right<s.size();right++)
        {
            hash2[s[right]]++;
            //hash2[in]<=hash1[in]说明right指向的字符是有效字符
            if(hash2[s[right]]<=hash1[s[right]])
            {
                count++;
            }
            //left与right区间超过len值进行left向右移动一次
            if(right-left+1>len)
            {
                //left要移动之前先检查left指向的字符是不是有效字符
                if(hash2[s[left]]<=hash1[s[left]])
                {
                    count--;
                }
                hash2[s[left]]--;
                left++;
            }
            //left与right之间的字符符合p的条件
            if(count==len)
            {
                ans.push_back(left);
            }
        }
        return ans;
    }
};

七串联所有单词的子串

oj链接:串联所有单词的子串

与上一道题思路类似,只不过是把字符变成字符串而已,自己动手尝试吧!

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        //用哈希表存储单词
        unordered_map<string,int> hash;
        vector<int> ans;
        for(auto& ch:words)
        {
            hash[ch]++;
        }
        int n=words[0].size();
        for(int i=0;i<n;i++)
        {
            unordered_map<string,int> hash1;
            for(int left=i,right=i,count=0;right+n<=s.size();right+=n)
            {
                string in=s.substr(right,n);
                hash1[in]++;
                if(hash1[in]<=hash[in])
                {
                    count++;
                }
                if(right-left+1>words.size()*n)
                {
                    string out=s.substr(left,n);
                    if(hash1[out]<=hash[out])
                    {
                        count--;
                    }
                    hash1[out]--;
                    left+=n;
                }
                if(count==words.size())
                {
                    ans.push_back(left);
                }
            }
        }
        return ans;

    }
};

八最小覆盖子串

oj链接:最小覆盖子串

思路:先用数组统计p中字符,用kind记录p中字符的种类。(与上面的不同)

滑动窗口:

定义两个‘指针’left与right

进窗口:

用right进行向右搜索,再用数组统计right走过的字符,

用count记录right走过的字符中是属性p的字符种类

(循环)判断:count==kind说明left与right的区间符合条件

更新结果,记录符合条件中最短的子串与left的位置

出窗口,(如果left当前指向的字符刚好是p中字符个数,count--)left向右走(跳出循环)

class Solution {
public:
    string minWindow(string s, string t) {
        //用kind来记录t字符串中的种类,hash1统计t中字符串的字符
        int kind=0,hash1[128]={ 0 };
        for(auto ch : t)
        {
            if(hash1[ch]==0)
            {
                kind++;
            }
            hash1[ch]++;
        }
        //hash2统计s中字符个数,count统计right维护区间内字符是t中字符种类相同的值
        //pos记录最短字符串的起始位置
        int hash2[128]={ 0 },count=0,minlen=INT_MAX,pos=-1;
        for(int left=0,right=0,count=0;right<s.size();right++)
        {
            //进窗口
            int ch=s[right];
            hash2[ch]++;
            //加完相等说明是t中字符的种类
            if(hash2[ch]==hash1[ch])
            {
                count++;
            }
            
            while(count==kind)
            {
                //更新结果
                if(right-left+1<minlen)
                {
                    minlen=right-left+1;
                    pos=left;
                }
                
                int out=s[left];
                if(hash2[out]==hash1[out])
                {
                    count--;
                }
                //出窗口
                hash2[out]--;
                left++;
            }
        }
        //找不到最短字符
        if(pos==-1)
        {
            return "";
        }
        return s.substr(pos,minlen);
    }
};

  • 28
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值