【知识讲解-题目讲解】 统计满足K约束的子字符串数量2 (滑动窗口+前缀和+二分查找+分析)


目录

前言

[Part1. 题目概览](#Part1. 题目概览)

[Part2. 题目分析](#Part2. 题目分析)

[Part3. 公式](#Part3. 公式)

[Part4. 优化算法](#Part4. 优化算法)

[Part5. 代码与细节](#Part5. 代码与细节)

[Part6. 结语](#Part6. 结语)


前言

Leetcode的这道题目很有综合性,涵盖了很多基础知识,我们来看看吧。


let's go!!!!!!!!


Part1. 题目概览


题目看上去还是比较简单的,就是统计在一个大字符串中的几个小字符串中满足条件的子字符串数量。

其中这个的数据规模是十的五次方,也就是说这个是NlogN级别的算法,所以暴力就被排除了。


Part2. 题目分析


现在我们知道了基本的基本的计算方法,我们来总结一下就可以得出公式,之后我们再根据公式再来优化算法。


Part3. 公式



Part4. 优化算法

我们对于每一个点都进行枚举,显然是不行的,这是N平方级别的时间复杂度,因此我们要优化。
首先,我们从pi来入手,我们可以先预处理一下整个的数组,将每个点对应的pi求出来,这样我们在具体计算的时候就是O(1)的知道pi的位置。
其次我们从min来入手,这里为什么要加个min?因为我们不知道什么时候pi会大于R,导致计算的方式改变,但是我们可以发现一个性质,就是当我们的i增加,pi也一定会增加(或者不变,总之不会减少),因此这个具有单调性,因此我们可以用二分查找,找到第一个pi大于R的i位置,也就是说,在这个i之前,我们计算就是按照pi-1-i+1来,后面就是按照R-i+1来,这样有什么好处呢?我们来看:


Part5. 代码与细节

cpp 复制代码
class Solution {
public:
    vector<long long> countKConstraintSubstrings(string s, int k,vector<vector<int>>& queries) {
        int n = s.size();

        vector<long long> sum(s.size() + 1);//前缀和数组
        vector<long long> p(s.size(),n);//p[i]数组 提前初始化为n

        vector<int> num(2,0);//统计0 1出现的次数

        {
            int right=0,left=0;

            for (; right < n; right++) //利用滑动窗口来填充p数组和前缀和数组
            {
                num[s[right]-'0']++;
                while (num[0]>k&&num[1]>k) 
                {
                    num[s[left]-'0']--;
                    p[left] = right;//此时right就是非法位置 直接赋值
                    sum[left+1]=sum[left]+right-1-left+1;//此时right为非法位置 要减去1才是合法的
                    left++;
                }
            }

            for(;left<n;left++)//在循环结束时 left还没到n 要让其跑完 填充 由于p数组已经在初始化的时候就已经赋值为n了 就不用这个在赋值
            {
                sum[left+1]=sum[left]+right-1-left+1;
            }

        }

        vector<long long> ans(queries.size());

        for(int i=0;i<queries.size();i++)
        {
            int left=queries[i][0],right=queries[i][1];//左右边界

            int pos = upper_bound(p.begin(),p.end(),right)-p.begin();//二分查找 找第一个p[i]大于(不是大于等于 不是lower_upper())R的i

            if(pos<=left)//值得注意的是 这个pos可能在L的左边 这时候就按照R-i+1 直接等差数列求和
                ans[i]=(long long)(right-left+1)*(right-left+2)/2;
            else
                ans[i]=sum[pos]-sum[left]+(right-pos+1)*(right-pos+2)/2;
        }

        return ans;
    }
};

Part6. 结语

这道题综合了基本算法:滑动窗口、二分查找、前缀和。是复习和学习这些算法的好题。
最后,祝大家可以:春风得意马蹄疾,一日看尽长安花!

最后的最后,要是觉得本文还可以的话,可以点点赞,关注小编一波,谢谢大家!~

相关推荐
通信小呆呆5 小时前
电子战抗干扰技术:当“不被干扰”成为一种系统
算法·通信·mimo·isac
笑容掩饰我的失落5 小时前
区块链从小白到精通(八):从零实现一个标准 ERC-20 代币(完整可运行代码)
算法·区块链·php·智能合约·哈希算法
Severus_black5 小时前
【C++初阶】Stack、Queue算法题大练兵!
c语言·c++·算法·stl
2601_967979128 小时前
机器学习必修课:经典算法与Python实战
python·算法·机器学习·实战·课程
程序猿打工仔8 小时前
计算机基础(C语言)考编、专技岗
c语言·开发语言·算法
通信小呆呆9 小时前
电子战干扰技术:从“蛮力压制”到“精准欺骗”,再到“认知博弈”
算法·通信·isac
老师我太想进步了20269 小时前
【数据结构-6】算法美学之单链表(1)
数据结构·算法
希望我万事顺遂10 小时前
力扣hot100-滑动窗口最大值
数据结构·算法·leetcode
꧁细听勿语情꧂10 小时前
二叉树余题,排序、直接插入排序、希尔排序
c语言·开发语言·数据结构·算法