Leetcode100子串

560.和为K的子数组

题目
我的思路:暴力

但是边界的问题一直有bug

cpp 复制代码
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int res=0;
        for(int i=0;i<nums.size();i++)
        {
            int sum=nums[i];
               if(sum==k)
                {
                    res++;
                 //   continue;
                }
              
             for(int j=i+1;j<nums.size();j++)
             {
                sum+=nums[j];
                 if(sum==k)
                {
                    res++;                   
                }
                
              }                         
        }
       
        return res;
    }
};

别人的题解
题解

方法一:暴力枚举思路:

  • 三重循环:
    一层循环枚举做指针left,一层循环枚举right指针,最内层循环遍历left到right之间的连续字串,看是否和为k。
  • 两重循环(和我的思路一样,精简了,但是超时了)
cpp 复制代码
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int res=0;
        for(int i=0;i<nums.size();i++)
        {
          /*  int sum=nums[i];
               if(sum==k)
                {
                    res++;
                }*/
              
              int sum=0;
             for(int j=i;j<nums.size();j++)//这里直接将j=i+1改成j=i,把sum每次都从0开始
             {
                sum+=nums[j];
                 if(sum==k)
                {
                    res++;                   
                }
                
              }                         
        }
       
        return res;
    }
    
};

官方的:看[j...i]是否和为k

cpp 复制代码
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int res=0;
        for(int i=0;i<nums.size();i++)
        {        
              int sum=0;
             for(int j=i;j>=0;j--)//[j...i]是否满足和为k
             {   sum+=nums[j];
                 if(sum==k)
                {
                    res++;                   
                }
                
              }                         
        }
       
        return res;
    }
};

方法二:前缀和+哈希

本质优化上面的,pre[i]为[0...i]的前缀和,计算[j...i]的和为公式:pre[i]-pre[j-1]==k,即pre[j-1]=pre[i]-k;

我们遍历的时候从左向右进行,使用i进行遍历,用哈希unordered_map<int,int> mp;存储<前缀和''i-k'',前缀和''i-k''出现的次数>,即看是否有pre[j]存在,存在的话说明存在[j...i]的子数组。

【评论区补充】:

前缀和的概念 首先,我们使用一个叫做"前缀和"的概念。对于数组中的任何位置 j,前缀和 pre[j] 是数组中从第一个元素到第 j 个元素的总和。这意味着如果你想知道从元素 i+1 到 j 的子数组的和,你可以用 pre[j] - pre[i] 来计算。

使用 Map 来存储前缀和 在代码中,我们用一个 Map(哈希表)来存储每个前缀和出现的次数。这是为了快速检查某个特定的前缀和是否已经存在,以及它出现了多少次。

核心逻辑解析 当我们在数组中向前移动时,我们逐步增加 pre(当前的累积和)。对于每个新的 pre 值,我们检查 pre - k 是否在 Map 中。

cpp 复制代码
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int res=0,pre=0;
        unordered_map<int,int> mp;
        mp[0]=1; //刚开始第一个前缀出现的次数为1
        for(int i=0;i<nums.size();i++)
        {    
            pre+=nums[i]; 
            if(mp.find(pre-k)!=mp.end())
            {
                res+=mp[pre-k];
            }
            mp[pre]++; //先查询后再将当前pre存储
                                  
        }
       
        return res;
    }
};

239. 滑动窗口最大值

题目

这个题目和上面的题目有点类似,都是三重循环,一层是总的遍历数量,一层left,一层right遍历left到right之间的(k长度)

方法一:暴力超时了

方法二:优先队列。

对方法一的内层循环降低复杂度,其实通过priority_queue这个数据结构来找最大值,双指针还是两重循环,只是优先队列底层是堆,复杂度是O(logn)

方法三:单调队列

双指针i,j也可以是双端队列,左边i,右边j。单调队列用双端队列来实现。在二的基础上又降低了一层复杂度。

cpp 复制代码
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
       deque<int> q;
       //单调递减
       for(int i=0;i<k;i++)
       {
        while(!q.empty()&&nums[i]>=nums[q.back()])
        {
            q.pop_back();
        }
        q.push_back(i);
       }
              vector<int> res={nums[q.front()]};

            for(int i=k;i<nums.size();i++)
            {
               while(!q.empty()&&nums[i]>=nums[q.back()])
               {
                   q.pop_back();
               }
               q.push_back(i);
               while(q.front()<=i-k)
               {
                q.pop_front();
               }
               res.push_back(nums[q.front()]);
            }        

        return res;
        
    }
};

76. 最小覆盖字串

题目

没有思路,不知道怎么判断滑动窗口里的字串字母个数是否小于等于t里的字母个数

这里官方用的哈希unordered_map,没有用vector v(26)
下面的评论区的解:FearnDreams看懂了:

  • 使用一个单独的函数检查是否滑动窗口里的和s里的个数大于等于
  • 检查的时机:当滑动窗口里新加入的字符在t里的时候就检查
  • 注意使用s.substr(startpos,len);来截取,而不是用一个res变量保存每次最小的,所以用了res_left来保存每次滑动窗口最左边的位置。
cpp 复制代码
class Solution {
public:
    //定义两个哈希表,tstr用来存放t中元素的出现次数信息,sstr用来存放滑动窗口中元素的出现次数信息
    unordered_map<char,int> tstr,sstr;

    //检查当前的窗口是否是合格的窗口,即:
    //检查当前滑动窗口中元素是否完全覆盖了字符串t中的所有元素(重点是某元素的出现次数必须不小于t中对应元素的出现次数)
    bool check()
    {
        for(auto tchar : tstr)
            {
                if(tchar.second > sstr[tchar.first]) return false;//注意这里的判断条件是大于
                //只要sstr中元素的second值不小于tchar中对应元素的second值就行
            }
        return true;
    }

    string minWindow(string s, string t) {
        int n1 = s.size(),n2 = t.size();
        if(n1<n2) return "";//如果t串比s串还长,s中肯定不能找到相应的子串
        int len = INT_MAX;//最小窗口的长度
        int ans_left = -1;//最小窗口的左边界指针
        //构造哈希表
        for(auto tchar : t)
            ++tstr[tchar];
        
        int left = 0,right = 0;//窗口的左右两端指针
        //滑动窗口右端指针遍历整个s串
        for(int right = 0;right<n1;right++)
        {   
            //每遍历一个元素,更新sstr中的元素信息
            ++sstr[s[right]];
            //如果当前遍历到的元素在tstr中也有,说明此次遍历的更新是有效的更新,否则不用管,直接继续遍历
            if(tstr.find(s[right]) != tstr.end())
            { 
                //对于每一次有效的更新,检查当前窗口中的子串是否是一个合格的子串
                while(check() && left<=right)
                {
                    //如果当前子串是合格的,那么判断是否是最小的窗口
                    if(len > right - left +1)
                    {
                        //如果是最小的窗口,那么更新窗口信息
                        ans_left = left;
                        len = right - left + 1;
                    }

                    //当前子串如果是合格的,那么尝试移进窗口的左边界缩短窗口的长度
                    --sstr[s[left]];//窗口左边界的元素信息从哈希表sstr中删除
                    left++;//移进窗口左边界

                    //移进之后,继续while判断移进后的子串是否是合格的,如果是合格的,继续重复同样的操作,更新窗口的信息
                }

                //一旦窗口不合格,窗口右边界的指针就继续往后遍历,拓宽窗口的长度
            }
        }
        if(ans_left == -1) return "";
        else return s.substr(ans_left,len);
    }
};
相关推荐
kcwqxx21 分钟前
day23|leetCode 39. 组合总和 , 40.组合总和II , 131.分割回文串
c++·算法·leetcode
kitesxian26 分钟前
Leetcode155. 最小栈(HOT100)
算法
一颗青果30 分钟前
【Linux】详解shell代码实现(上)
linux·运维·服务器·前端·chrome·算法·1024程序员节
Best_Me0743 分钟前
快速排序算法-C语言
c语言·算法·排序算法
Tianwen_Burning1 小时前
halcon3D 1:1切片轮廓投影技术,透过像素距离,知实际物体的尺寸
算法·3d
pursuit_csdn2 小时前
LeetCode 1072. Flip Columns For Maximum Number of Equal Rows
数据结构·算法·leetcode·力扣
ahadee2 小时前
蓝桥杯每日真题 - 第23天
c语言·vscode·算法·蓝桥杯
花糖纸木2 小时前
算法练习:76. 最小覆盖子串
c++·算法
冉佳驹2 小时前
数据结构 ——— 直接选择排序算法的实现
c语言·数据结构·算法·排序算法·直接选择排序算法
川石课堂软件测试2 小时前
UI自动化测试|web端元素获取&元素等待实践
开发语言·前端·功能测试·算法·ui