滑动窗口(二)

目录

一.最大连续1的个数 III

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

三.水果成篮

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

五.最小覆盖子串(. - 力扣(LeetCode)) 

六。串联所有单词的子串(. - 力扣(LeetCode))


一.最大连续1的个数 III

算法思想:

不断入窗口,如果扫描为0,则可看作翻转为1,计数器cnt++,然后判断计数器反转次数是否大于K,大于则出窗口直到出掉一个0使cnt--,然后重新记录长度len取最大值即可。

class Solution {
public:
    int longestOnes(vector<int>& nums, int k){
        int cnt=0,len=0;
        int left=0,right=0;
        while(right<nums.size())
        {
            //进窗口
            if(nums[right]==0)
            {
                cnt++;
            }
            //判断
            if(cnt>k)
            {
                //出窗口
                while(left<right&&nums[left]!=0)
                {
                    left++;
                }
                left++;
                cnt--;
            }
            //重新记录
            len=max(len,right-left+1);
            right++;
        }
        return len;
    }
};

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

 算法思想:正难则反,这里可以先将数组的sum算出,然后sum-x算出target即滑动窗口的和,然后保证滑动窗口内和为target,求最大长度的滑动窗口len,最后返回num.size()-len即可。

PS:这里先将len设为-1,如果最后len是否-1要分类讨论。

class Solution {
public:
    int minOperations(vector<int>& nums, int x){
        //正难则反
        int sum=0;
        for(int i=0;i<nums.size();i++)
            sum+=nums[i];

        //滑动窗口的和值
        sum-=x;

        //求最长滑动窗口
        int left=0,right=0;
        int len=-1,win_sum=0;
        while(right<nums.size())
        {
            //进窗口
            win_sum+=nums[right];
            //判断
            while(left<=right&&win_sum>sum)
            {
                //出窗口
                win_sum-=nums[left];
                left++;
            }
            //如果符合就记录结果
            if(win_sum==sum)
                len=max(len,right-left+1);
            right++;
        }
        if(len==-1)
        return -1;
        else
        return nums.size()-len;
    }
};

三.水果成篮

算法思想:可以用哈希表记录是否已经采摘过扫描指针指向的水果(入窗口),如果没有,则令type++,然后判断当type>2时,则将left++(出窗口),令哈希表中left指向的水果种类-1直到为0时,type--,保证type<=2,然后记录结果长度len。

PS:我们将结果记录放在判断之后是因为,我们认为扫描指针前的结果已经记录下来,所以只需从扫描指针开始再次更新结果即可。

class Solution {
public:
    int totalFruit(vector<int>& fruits)
    {
        int hash[100100]={0};

        int left=0,right=0;
        int len=0,type=0;
        while(right<fruits.size())
        {
            //进窗口
            hash[fruits[right]]++;
            //判断
            if(hash[fruits[right]]==1)
            {
                //种类数+1
                type++;
                while(type>2)
                {
                    //出窗口
                    hash[fruits[left]]--;
                    if(hash[fruits[left]]==0)
                        type--;
                    left++;
                }
            }
            //记录结果
            len=max(len,right-left+1);
            right++;
        }
        return len;
    }
};

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

算法思想:依旧用哈希表,先记录p子串哈希表,然后滑动窗口遍历s串,当s的哈希数据开始大于p时,开始出窗口直到符合位置,当窗口长度与p长度相等时说明符合,记录当前索引。

class Solution {
public:
    vector<int> findAnagrams(string s, string p)
    {
        vector<int> res;
        //提前判断
        if(s.size()<p.size())
        return res;
        int hash1[128]={0};
        int hash2[128]={0};
        //先用哈希表记录子串的数据,不在乎顺序
        for(int i=0;i<p.size();i++)
           hash1[p[i]]++;

        int left=0,right=0;
        while(right<s.size())
        {
            //进窗口
            hash2[s[right]]++;
            //判断,当与子串某一位置的哈希数据不符时则出窗口
            while(left<=right&&hash2[s[right]]>hash1[s[right]])
            {
                //出窗口
                hash2[s[left]]--;
                left++;
            }
            //当长度相等时说明子串匹配
            if(right-left+1==p.size())
            {
                res.push_back(left);
            }
            right++;
        }   
        return res; 
    }
};

五.最小覆盖子串(. - 力扣(LeetCode)) 

算法思想:用哈希表记录t数据,用滑动窗口遍历s串,每次入窗口时检查一下是否窗口的哈希数据全部大于等于t,如果成立说明窗口已经覆盖t串,记录数据(索引)并开始出窗口直到不符为止跳出判断。最后找到最小长度的索引,拷贝一份返回即可。

bool check(int*hash1,int*hash2)
{
    for(int i=0;i<128;i++)
    {
        if(hash2[i]<hash1[i])
        return false;
    }
    return true;
}

class Solution {
public:
    string minWindow(string s, string t)
    {
        string a("");
        if(s.size()<t.size())
        return a;
        int hash1[128]={0};
        int hash2[128]={0};
        for(int i=0;i<t.size();i++)
        {
            hash1[t[i]]++;
        }

        int left=0,right=0;
        int len=INT_MAX,mark=-1;
        while(right<s.size())
        {
            //进窗口
            hash2[s[right]]++;

            //判断
            while(check(hash1,hash2))
            {
                //更新结果
                if(right-left+1<rlen)
                {
                    len=right-left+1;
                    mark=left;
                }
                //出窗口
                hash2[s[left]]--;
                left++;
            }
            right++;
        }
        if(mark==-1)
        return a;
        else
        return s.substr(mark,len);
    }
};

六。串联所有单词的子串(. - 力扣(LeetCode)

算法思想:因为word中所有单词长度相等,可以将每个单词看作不同的abc字母,按照第四题的异位词解题方法来用哈希表解决问题(以单词长度len为单位移动,经过len次遍历)。

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words)
    {
        vector<int> ret;
        unordered_map<string,int> hash1;//记录每个单词出现的频次(以单词为单位)
        for(auto &s:words)
            hash1[s]++;//不考虑单词内部具体顺序,只关注单词整体的哈希数据
        //len为单词的长度也即要总的遍历的次数,m为单词的个数
        int len=words[0].size(),m=words.size();
        for(int i=0;i<len;i++)
        {
            //记录s串遍历过程中窗口内的哈希数据
            unordered_map<string,int> hash2;
            //记录窗口内已经匹配的单词个数
            int count=0;
            for(int left=i,right=i;right+len<=s.size();right+=len)
            {
                //入窗口+维护conut
                string in=s.substr(right,len);
                hash2[in]++;
                //输入的单词匹配则增加count个数,说明已经匹配上
                if(hash1.count(in)&&hash2[in]<=hash1[in]) count++;
                //判断,当窗口长度大于words中所有单词长度串联总和时出窗口
                while(right-left+1>len*m)
                {
                    //出窗口+维护count
                    string out=s.substr(left,len);
                    //事先判断words数组里有无这个扫描单词,节省时间开销
                    if(hash1.count(out)&&hash2[out]<=hash1[out]) count--;
                    hash2[out]--;
                    left+=len;
                }
                //更新结果,当窗口有m个元素单词匹配时,说明完全匹配,记录索引
                //这里保证了窗口长度不可能大于所有单词长度串联总和,所以不会有其他多余的无关单词
                if(count==m) ret.push_back(left);
            }
        }
        return ret;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值