【滑动窗口】dd爱框框 / 水果成篮 / 找到字符串中所有字母异位词 / 串联所有单词的子串 / 最小覆盖子串

头像
⭐️个人主页:@小羊
⭐️所属专栏:基础算法
很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~

动图描述

长度最小的子数组

在这里插入图片描述

题目要求我们求子数组和>=目标值的最小长度,即需要在满足条件的情况下统计最小长度。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size(), ret = INT_MAX, sum = 0;
        for (int l = 0, r = 0; r < nums.size(); r++)
        {
            sum += nums[r];
            while (sum >= target)
            {
                ret = min(ret, r - l + 1);
                sum -= nums[l++];
            }
        }
        return ret == INT_MAX ? 0 : ret;
    }
};

无重复字符的最长子串

在这里插入图片描述

用数组模拟哈希表,统计进入窗口的字符的个数,以便统计最长子串的长度。
注意统计长度的位置,应该在不含有重复字符后统计结果,和前面一题区分。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int hash[200] = {};
        int n = s.size(), len = 0;
        for (int l = 0, r = 0; r < n; r++)
        {
            hash[s[r]]++; // 进窗口
            while (hash[s[r]] > 1)
            {
                hash[s[l++]]--; // 出窗口
            }
            len = max(len, r - l + 1);
        }
        return len;
    }
};

dd爱框框

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n, x;
    cin >> n >> x;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++)
            cin >> a[i];
    int left = -1, right = -1, sum = 0, len = INT_MAX;
    for (int l = 1, r = 1; r <= n; r++)
    { 
        sum += a[r]; // 1.进窗口
        while (sum >= x) // 2.判断
        {
            if (r - l + 1 < len)
            {
                // 3.更新结果
                left = l, right = r;
                len = right - left + 1;
            }
            sum -= a[l++]; // 4.出窗口
        }
    }
    cout << left << " " << right << endl;
    return 0;
}

最大连续1的个数 III

在这里插入图片描述

反着来思考,翻转k个0让数组连续1最长,也就是允许一段连续1的子数组里有k个0,可以维护一段窗口,其中的0的个数不超过k就行。

class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int n = nums.size(), ret = 0, zero = 0;
        for (int l = 0, r = 0; r < n; r++)
        {
            if (nums[r] == 0) zero++; // 进窗口
            while (zero > k) // 当条件不满足时
            {
                if (nums[l++] == 0) zero--; // 出窗口
            }
            ret = max(ret, r - l + 1); // 统计结果
        }
        return ret;
    }
};

将 x 减到 0 的最小操作数

在这里插入图片描述

减去数组左边或右边的元素到刚好为给定的x,正着不好求可以考虑反着来,求数组中连续的最长子数组,其和为整个数组之和-x。

class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int n = nums.size(), sum = 0, len = -1, tmp = 0;
        for (auto e : nums) sum += e;
        int target = sum - x; 
        if (target < 0) return -1;
        for (int l = 0, r = 0; r < n; r++)
        {
            tmp += nums[r];
            while (tmp > target)
            {
                tmp -= nums[l++];
            }
            if (tmp == target)
                len = max(len, r - l + 1);
        }
        return len == -1 ? len : n - len;
    }
};

水果成篮

在这里插入图片描述

用数组模拟哈希表统计水果种类个数,如果哈希表中水果种类对应位置为0则种类++,出窗口哈希表中对应位置值–,当减小到0时种类减一。

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        int hash[100001] = {};
        int n = fruits.size(), len = 0, kinds = 0;
        for (int l = 0, r = 0; r < n; r++)
        {
            if (hash[fruits[r]] == 0) kinds++;
            hash[fruits[r]]++; // 进窗口
            while (kinds > 2) // 条件判断
            {
                if (--hash[fruits[l++]] == 0) kinds--; // 出窗口,跟新条件
            }
            len = max(len, r - l + 1); // 更新结果
        }
        return len;
    }
};

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

在这里插入图片描述

在这里插入图片描述

用一个count变量统计窗口中对应p中字符的个数,只有当窗口大小等于p的长度,并且对应字符个数相同才符合要求。

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        int hash1[26] = {};
        int n = p.size();
        for (auto ch : p) hash1[ch - 'a']++; // 统计p中各字符出现的次数
        int hash2[26] = {};
        vector<int> ret;
        for (int l = 0, r = 0, count = 0; r < s.size(); r++)
        {
            // 进窗口
            if (++hash2[s[r] - 'a'] <= hash1[s[r] - 'a']) count++;
            while (r - l + 1 > n)
            {
                int t = s[l++] - 'a'; // 出窗口
                if (hash2[t]-- <= hash1[t]) count--; 
            }
            if (count == n) ret.push_back(l); // 窗口内对应字符个数等于p的长度才符合要求
        }
        return ret;
    }
};

串联所有单词的子串

在这里插入图片描述

在这里插入图片描述

本质还是和上一题字母异位词一样,从目标字符串中找指定个子串任意排列的连接的子串,只不过本题需要枚举子串长度遍,在遍历的过程中注意范围,和一步需要走多长的距离。

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        unordered_map<string, int> hash1;
        for (auto& e : words) hash1[e]++;
        int len = words[0].size(), n = words.size();
        vector<int> ret;
        for (int i = 0; i < len; i++) // 以[0, len)中的位置为起点
        {
            unordered_map<string, int> hash2; // hash2每次都要更新
            // 注意边界处理,保证能构成一个长度为len的子串
            for (int l = i, r = i, count = 0; r + len <= s.size(); r += len)
            {
                string in = s.substr(r, len); 
                if (++hash2[in] <= hash1[in]) count++;
                while (r - l + 1 > n * len)
                {
                    string out = s.substr(l, len);
                    l += len; // 出窗口
                    if (hash2[out]-- <= hash1[out]) count--;
                }
                if (count == n) ret.push_back(l);
            }
        }
        return ret;
    }
};

最小覆盖子串

在这里插入图片描述

在这里插入图片描述

本题和上面的两题有点区别,上面两题是字符或子串拼接而成,其中的字符和子串对应的个数必须是相同的,但本题要找的是覆盖所有字符的最小子串,字符对应的个数可以不相等,但必须保证每个字符都存在。

class Solution {
public:
    string minWindow(string s, string t) {
        int hash1[128] = {};
        int kinds = 0; // t中字符种类个数
        for (auto e : t)
            if (hash1[e]++ == 0)
                kinds++;
        int hash2[128] = {};
        int begin = -1, len = INT_MAX;
        for (int l = 0, r = 0, count = 0; r < s.size(); r++)
        {
            // 进窗口
            char in = s[r];
            // 当窗口中in字符的个数等于t中in字符的个数时
            if (++hash2[in] == hash1[in]) count++;
            while (count == kinds)
            {
                if (r - l + 1 < len)
                {
                    len = r - l + 1;
                    begin = l;
                }
                // 出窗口
                char out = s[l++];
                if (hash2[out]-- == hash1[out]) count--;
            }
        }
        return begin == -1 ? "" : s.substr(begin, len);
    }
};
class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char, int> hash1;
        auto hash2 = hash1;
        for (auto e : t) hash1[e]++;
        int begin = 0, len = INT_MAX;
        for (int l = 0, r = 0, count = 0; r < s.size(); r++)
        {
            if (++hash2[s[r]] <= hash1[s[r]]) count++;
            while (count == t.size())
            {
                if (r - l + 1 < len)
                {
                    len = r - l + 1;
                    begin = l;
                } 
                if (hash2[s[l]]-- <= hash1[s[l++]]) count--;
            }
        }
        return len == INT_MAX ? "" : s.substr(begin, len);
    }
};

DNA序列

在这里插入图片描述

固定长度的滑动窗口问题,当满足条件时更新结果。

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string str;
    int n;
    cin >> str >> n;
    int len = str.size();
    if (len <= n) 
    {
        cout << str << endl;
        return 0;
    }
    int cnt = 0;
    for (int i = 0; i < n; i++)
        if (str[i] == 'G' || str[i] == 'C')
            cnt++;
    int begin = 0, tmp = cnt;
    for (int l = 0, r = n - 1; r + 1 < len;)
    {
        char a = str[l++], b = str[++r];
        if (a == 'G' || a == 'C') tmp--;
        if (b == 'G' || b == 'C') tmp++;
        if (tmp > cnt) 
        {
            begin = l;
            cnt = tmp;
        }
    }
    cout << str.substr(begin, n) << endl;
    return 0;
}

本篇文章的分享就到这里了,如果您觉得在本文有所收获,还请留下您的三连支持哦~

头像
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值