每日算法刷题Day16 5.26:leetcode不定长滑动窗口求子数组个数越短越合法2道题+恰好型3道题,用时1h20min

4. LCP 68.美观的花束(中等)

LCP 68. 美观的花束 - 力扣(LeetCode)

思想

1.你可以选择一段区间的鲜花做成插花,且不能丢弃。 在你选择的插花中,如果每一品种的鲜花数量都不超过 cnt 朵,那么我们认为这束插花是 「美观的」。请返回在这一排鲜花中,共有多少种可选择的区间,使得插花是「美观的」。

2.利用unordered_map记录每种花的次数即可

代码

c++:

复制代码
class Solution {
    const int mod = 1'000'000'007; // 用'分隔数字直观

public:
    int beautifulBouquet(vector<int>& flowers, int cnt) {
        int n = flowers.size();
        long long res = 0;
        unordered_map<int, int> sum;
        int left = 0;
        for (int right = 0; right < n; ++right) {
            ++sum[flowers[right]];
            while (left<=right && sum[flowers[right]] > cnt) {
                --sum[flowers[left]];
                ++left;
            }
            res = (res + right - left + 1) % mod;
        }
        return res;
    }
};
5. 2743.计算没有重复字符的子字符数量(中等)

2743. 计算没有重复字符的子字符串数量 - 力扣(LeetCode)

思想

1.给定你一个只包含小写英文字母的字符串 s 。如果一个子字符串不包含任何字符至少出现两次(换句话说,它不包含重复字符),则称其为 特殊 子字符串。你的任务是计算 特殊 子字符串的数量。

代码

c++:

复制代码
class Solution {
public:
    int numberOfSpecialSubstrings(string s) {
        int n = s.size();
        long long res = 0;
        unordered_map<char, int> cnt;
        int left = 0;
        for (int right = 0; right < n; ++right) {
            ++cnt[s[right]];
            while (left<=right && cnt[s[right]] > 1) {
                --cnt[s[left]];
                ++left;
            }
            res += right - left + 1;
        }
        return res;
    }
};
2.3.3 恰好型滑动窗口

例如,要计算有多少个元素和恰好等于 k 的子数组,可以把问题变成:

  • 计算有多少个元素和 ≥k 的子数组。
  • 计算有多少个元素和 >k,也就是 ≥k+1 的子数组。
    答案就是元素和 ≥k 的子数组个数,减去元素和 ≥k+1 的子数组个数 。这里把 > 转换成 ≥,从而可以把滑窗逻辑封装成一个函数 f,然后用 f(k) - f(k + 1) 计算,无需编写两份滑窗代码。
    总结:「恰好」可以拆分成两个「至少」,也就是两个「越长越合法」的滑窗问题
    注:也可以把问题变成 ≤k 减去 ≤k−1(两个至多)。可根据题目选择合适的变形方式。
    注:也可以把两个滑动窗口合并起来,维护同一个右端点 right 和两个左端点 l e f t 1 left_1 left1
    和 l e f t 2 left_2 left2,我把这种写法叫做三指针滑动窗口。
模版套路
套路
  • 1.写成函数型

    class Solution {
    public:
    // 越长越合法滑动窗口
    long long f(vector<int>& nums, int k){
    long long res=0;
    int n=nums.size();
    long long sum=0;
    int left=0;
    for(int right=0;right<n;++right){
    sum+=nums[right];
    // 变为sum>=k
    while(left<=right && sum>=k){
    sum-=nums[left];
    ++left;
    }
    res+=left;
    }
    return res;
    }
    int numSubarraysWithSum(vector<int>& nums, int goal) {
    // 转化为和>=goal的子数组数量减去>=goal+1的子数组数量,转化为越长越合法型
    int res=f(nums,goal)-f(nums,goal+1);
    return res;
    }
    };

2.两个left指针型

复制代码
class Solution {
public:
    int numSubarraysWithSum(vector<int>& nums, int goal) {
        long long res=0; 
        int n=nums.size();
        long long sum1=0,sum2=0; //sum1为>=goal,sum2为>=goal+1
        int left1=0,left2=0; //left1为>=goal,left2为>=goal+1
        for(int right=0;right<n;++right){
            sum1+=nums[right];
            sum2+=nums[right];
            // 变为sum1>=goal
            while(left1<=right && sum1>=goal){
                sum1-=nums[left1];
                ++left1;
            }
            // 变为sum2>=goal+1
            while(left2<=right && sum2>=goal+1){
                sum2-=nums[left2];
                ++left2;
            }
            // 答案为left1-left2
            res+=(left1-left2);
        }
        return res;
        return res;
    }
};
题目描述

1.给你一个二元数组 nums ,和一个整数 goal ,请你统计并返回有多少个 和为 goal (窗口条件) 的 非空子数组。

2.给你一个整数数组 nums 和一个整数 k。如果某个连续子数组中恰好有 k 个奇数数字 (窗口条件),我们就认为这个子数组是「优美子数组」。请返回这个数组中 「优美子数组」的数目

3.给你一个字符串 word 和一个 非负 整数 k。返回 word 的 子字符串 中,每个元音字母('a''e''i''o''u')至少出现一次,并且 恰好 包含 k 个辅音字母 (窗口条件)的子字符串的总数

学习经验

1.转变为(>=k)-(>=k+1)两个越长越合法型

1. 930.和相同的二元子数组(中等,学习)

930. 和相同的二元子数组 - 力扣(LeetCode)

思想

1.给你一个二元数组 nums ,和一个整数 goal ,请你统计并返回有多少个和为 goal非空 子数组。

2.转化为和>=goal的子数组数量减去>=goal+1的子数组数量,转化为越长越合法型

3.写成越长越合法函数或者两个left指针

代码

c++ :

1.函数型

复制代码
class Solution {
public:
    // 越长越合法滑动窗口
    long long f(vector<int>& nums, int k){
        long long res=0;
        int n=nums.size();
        long long sum=0;
        int left=0;
        for(int right=0;right<n;++right){
            sum+=nums[right];
            // 变为sum>=k
            while(left<=right && sum>=k){
                sum-=nums[left];
                ++left;
            }
            res+=left;
        }
        return res;
    }
    int numSubarraysWithSum(vector<int>& nums, int goal) {
        // 转化为和>=goal的子数组数量减去>=goal+1的子数组数量,转化为越长越合法型
        int res=f(nums,goal)-f(nums,goal+1);
        return res;
    }
};

2.两个left指针型

复制代码
class Solution {
public:
    int numSubarraysWithSum(vector<int>& nums, int goal) {
        long long res=0; 
        int n=nums.size();
        long long sum1=0,sum2=0; //sum1为>=goal,sum2为>=goal+1
        int left1=0,left2=0; //left1为>=goal,left2为>=goal+1
        for(int right=0;right<n;++right){
            sum1+=nums[right];
            sum2+=nums[right];
            // 变为sum1>=goal
            while(left1<=right && sum1>=goal){
                sum1-=nums[left1];
                ++left1;
            }
            // 变为sum2>=goal+1
            while(left2<=right && sum2>=goal+1){
                sum2-=nums[left2];
                ++left2;
            }
            // 答案为left1-left2
            res+=(left1-left2);
        }
        return res;
        return res;
    }
};
2. 1248.统计优美子数组

1248. 统计「优美子数组」 - 力扣(LeetCode)

思想

1.给你一个整数数组 nums 和一个整数 k。如果某个连续子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组 」。请返回这个数组中 「优美子数组」 的数目。

代码

c++:

复制代码
class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        int n=nums.size();
        long long res=0;
        int cnt1=0,cnt2=0;
        int left1=0,left2=0;
        for(int right=0;right<n;++right){
            if(nums[right]&1){
                ++cnt1;
                ++cnt2;
            }
            while(left1<=right && cnt1>=k){
                if(nums[left1]&1)   --cnt1;
                ++left1;
            }
            while(left2<=right && cnt2>=k+1){
                if(nums[left2]&1)   --cnt2;
                ++left2;
            }
            res+=(left1-left2);
        }
        return res;
    }
};
3. 3306.元音辅音字符串计数II

3306. 元音辅音字符串计数 II - 力扣(LeetCode)

思想

1.给你一个字符串 word 和一个 非负 整数 k。返回 word 的 子字符串 中,每个元音字母('a''e''i''o''u'至少 出现一次,并且 恰好 包含 k 个辅音字母的子字符串的总数。

2."每个元音字母('a''e''i''o''u'至少 出现一次"是越长越合法型,"恰好 包含 k 个辅音字母"是恰好型,也转化为两个越长越合法型相减,所以while循环里面可以写两个条件相与,才是符合条件的窗口

代码

c++:

复制代码
class Solution {
public:
    long long countOfSubstrings(string word, int k) {
        long long res=0;
        int n=word.size();
        unordered_map<char,int> cnt1y,cnt2y;
        int cnt1f=0,cnt2f=0;
        int left1=0,left2=0;
        for(int right=0;right<n;++right){
            if(word[right]=='a' || word[right]=='e' || word[right]=='i' || word[right]=='o' || word[right]=='u'){
                ++cnt1y[word[right]];
                ++cnt2y[word[right]];
            }
            else{
                ++cnt1f;
                ++cnt2f;
            }
            while(left1<=right && cnt1y.size()==5 && cnt1f>=k){
                if(word[left1]=='a' || word[left1]=='e' || word[left1]=='i' || word[left1]=='o' || word[left1]=='u')    --cnt1y[word[left1]];
                else    --cnt1f;
                if(cnt1y[word[left1]]==0)   cnt1y.erase(word[left1]);
                ++left1;
            }
            while(left2<=right && cnt2y.size()==5 && cnt2f>=k+1){
                if(word[left2]=='a' || word[left2]=='e' || word[left2]=='i' || word[left2]=='o' || word[left2]=='u')    --cnt2y[word[left2]];
                else    --cnt2f;
                if(cnt2y[word[left2]]==0)   cnt2y.erase(word[left2]);
                ++left2;
            }
            res+=(left1-left2);
        }
        return res;
    }
};
相关推荐
数据与人工智能律师2 小时前
加密货币投资亏损后,能否以“欺诈”或“不当销售”索赔?
大数据·网络·算法·云计算·区块链
努力学习的小廉2 小时前
我爱学算法之—— 二分查找(下)
算法
AdSet聚合广告2 小时前
APP广告变现,开发者如何判断对接的广告SDK安全合规?
大数据·后端·算法·安全·uni-app
不二狗2 小时前
每日算法 -【Swift 算法】实现回文数判断!
开发语言·算法·swift
梁下轻语的秋缘5 小时前
Python人工智能算法 模拟退火算法求解01背包问题:从理论到实践的完整攻略
人工智能·python·算法·数学建模·模拟退火算法
Smile灬凉城6665 小时前
IPsec协议
考研·数学·算法
xsh2196 小时前
贪心算法套路模板+详细适用场景+经典题目清单
算法
白熊1886 小时前
【机器学习基础】机器学习入门核心算法:逻辑回归(Decision Tree)
算法·机器学习·逻辑回归
wen__xvn6 小时前
DFS入门刷题c++
c++·算法·深度优先
ruanjiananquan997 小时前
哈希算法:原理、应用、安全演进与推荐
算法·安全·哈希算法