coding ability 展开第四幕(滑动指针——巩固篇)超详细!!!!

coding ability 展开第四幕(滑动指针——巩固篇)超详细!!!!
在这里插入图片描述

文章目录

前言

本专栏上一篇博客,带着大家从认识滑动窗口到慢慢熟悉
相信大家对滑动窗口已经有了大概的认识
其实主要就是抓住——一段连续的区间
今天来学习一些滑动窗口进阶的题目
fellow me

水果成篮

在这里插入图片描述

思路

一开始看到这个题目,一段连续的区间,想到了滑动窗口
然后就想着怎么维护窗口,每次更新到新的水果种类就要,开始对left++,然后处理数据
其实是有点麻烦的,但是经过半个多小时的调试,最后还是ac了
思路:每次更新两个种类的水果,x,y,如果下一个水果的种类不相符合,就更新新的x,y
这个时候 right - 1 和 right 所对应的水果就是新的两种,然后就是处理从 left 到 right - 1这段区间
符合条件的情况, 也就是 从right - 1 一直往前走,到与 right - 1 所对应种类不同的水果,然后再更新结果
在反复进窗口,维护窗口,出窗口

代码如下 :

classSolution{public:inttotalFruit(vector<int>& fruits){int ans =0;int n = fruits.size();int x = fruits[0], y = fruits[0];if(n ==1)return1;int left =0, right =1;for(; right < n; right++){if(fruits[right]!= x && fruits[right]!= y)// 和前面确定的水果种类{ y = fruits[right -1];// 更新新的水果种类  x = fruits[right];int i = right -1;if(i != left)// left 到 right - 1 区间 {while(fruits[i]== y && i > left)// i一直靠近left 直到种类不同{ i--;}if(i == left && y != fruits[left])// 更新 left 的指向 left++;elseif(i != left) left = i +1;}} ans =max(ans, right - left +1);// 更新结果}return ans;}};

后面又想到了一种思路:
就是用哈希来统计种类数量,哈希相对于前面的那种方法还是简单的
就是把两个种类的水果放入哈希表,然后right++ 水果进窗口,如果哈希表的水果种类大于2
就从左侧 left 开始逐步出窗口,直到哈希表种类等于2,然后更新结果

代码如下:

classSolution{public:inttotalFruit(vector<int>& f){ unordered_map<int,int> hash;// 统计窗口内出现了多少种水果int ret =0;for(int left =0, right =0; right < f.size(); right++){ hash[f[right]]++;// 进窗口while(hash.size()>2)// 判断{// 出窗口 hash[f[left]]--;if(hash[f[left]]==0) hash.erase(f[left]); left++;} ret =max(ret, right - left +1);}return ret;}};

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

在这里插入图片描述

思路

看到又是一段连续的区间,就想到了滑动窗口
这一题 p 的异位词,说白了就是包含了 p 的所有字母,不管先后顺序
想到上一题用哈希优化的爽快,这题好像也可以用哈希来解
把 p 中字符都用哈希统计频次,然后在遍历 s 时,进窗口,维护窗口,出窗口
每次进入窗口,就用哈希统计出现次数,只要没有到达次数上限,就进窗口
如果进入窗口的字符数量大于了 p 的长度,就维护窗口从left开始往右开始出窗口
每一次统计进入窗口的数量时,都比较一下 p 的字符个数,如果进入窗口的字符个数等于 p的个数大小相等,就更新结果

代码如下:

classSolution{public: vector<int>findAnagrams(string s, string p){ vector<int> ret;int hash1[26]={0};// 统计字符串 p 中每个字符出现的个数for(auto ch : p) hash1[ch -'a']++;int hash2[26]={0};// 统计窗口里面的每一个字符出现的个数int m = p.size();for(int left =0, right =0, count =0; right < s.size(); right++){char in = s[right];// 进窗口 + 维护 countif(++hash2[in -'a']<= hash1[in -'a']) count++;if(right - left +1> m)// 判断{char out = s[left++];// 出窗口 + 维护 countif(hash2[out -'a']--<= hash1[out -'a']) count--;}// 更新结果if(count == m) ret.push_back(left);}return ret;}};

下面就上难度了嗷~~~~

串联所有单词的子串

在这里插入图片描述

思路

这个题目看起来很难,其实一点也不简单,看到困难的标签就让人望而生畏
但其实也没有想象的那么难,慢慢抽丝剥茧,其实也能拿下

在这里插入图片描述


看到这里,其实就想到了上一题的那个字母异位词,本题是说所有单词都包含,然后不管顺序
上一题是——所有字母都包含,然后不管顺序,我们不妨试试上一题的思路呢
首先要解决的就是把单词抽象变成字符,我们可以定义一个 string,映射 int 的 哈希表,这个问题就迎刃而解了
下一个问题就是怎么才能知道哪个是单词的开头字母呢??又怎么在 s 中遍历单词然后进窗口呢??

在这里插入图片描述


我又看到了这个条件,雪中送炭,所有单词长度相等,那岂不是起飞了,就让 right 每次遍历 += 单词长度就好了
这些都处理好了,最后一个问题就是,我怎么知道哪个字符是单词的第一个字母,错乱了怎么办,那不是gg
我们只需要在外面套一层for循环,从0到单词的长度,这段区间都做一次滑动窗口就好啦
核心问题都解决了,剩下的就是一些细节处理问题了
话不多说,这些都解决了,开始执行代码:

classSolution{public: vector<int>findSubstring(string s, vector<string>& words){ vector<int> ret; unordered_map<string,int> hash1;// 保存 words 里面所有单词的频次for(auto& c : words) hash1[c]++;int len = words[0].size(), m = words.size();for(int i =0; i < len; i++)// 执行 len 次{ unordered_map<string,int> hash2;// 维护窗口内单词的频次for(int left = i, right = i, count =0; right + len <= s.size();right += len){// 进窗口 + 维护 count string in = s.substr(right, len); hash2[in]++;if(hash1.count(in)&& hash2[in]<= hash1[in]) count++;// 判断if(right - left +1> len * m){// 出窗口 + 维护 count string out = s.substr(left, len);if(hash1.count(out)&& hash2[out]<= hash1[out]) count--; hash2[out]--; left += len;}// 更新结果if(count == m) ret.push_back(left);}}return ret;}};

其实hard题也是由一个一个小问题糅合起来的,解决核心问题,其实也没有那么难
慢慢啃下来,还是有成就感的 come on

最小覆盖子串

在这里插入图片描述

思路

又又又是一段连续的区间,来吧来吧,滑动窗口,滑动窗口

在这里插入图片描述


这道题不是上一题的恰好涵盖所有字符了,要返回的是最小子串,也就是能包含其他的
但是经过前面题目的磨砺,我感觉好像自己有点门道了
思路:用哈希1统计字符串 t 的每一个字符的频次,还有种类
构造一个新的哈希2,然后遍历 s 字符串,每个字符都统计到新的哈希表
当这个字符的频次和 哈希1中统计的字符频次相等时,种类数++
种类数和哈希1种类数相等时维护窗口,更新结果
大致思路就差不多是这样,来执行代码吧

classSolution{public: string minWindow(string s, string t){int hash1[128]={0};// 统计字符串 t 中每一个字符的频次int kinds =0;// 统计有效字符有多少种for(auto ch : t)if(hash1[ch]++==0) kinds++;int hash2[128]={0};// 统计窗口内每个字符的频次int minlen = INT_MAX, begin =-1;for(int left =0, right =0, count =0; right < s.size(); right++){char in = s[right];if(++hash2[in]== hash1[in]) count++;// 进窗口 + 维护 countwhile(count == kinds)// 判断条件{if(right - left +1< minlen)// 更新结果{ minlen = right - left +1; begin = left;}char out = s[left++];if(hash2[out]--== hash1[out]) count--;// 出窗口 + 维护 count}}if(begin ==-1)return"";elsereturn s.substr(begin, minlen);}};

这个困难也拿下啦,滑动窗口和哈希一起用感觉有点爽,绝佳组合

总结

滑动窗口的核心在于维护一段连续区间,通过哈希表优化统计与比较过程。面对不同问题需灵活调整:
哈希表记录元素频次,快速判断窗口合法性(如水果种类、异位词)
双指针动态伸缩窗口,确保时间复杂度为O(N)
多层循环处理定长元素(如单词串联),覆盖所有起点情况
种类与频次匹配时更新结果,最小子串问题需全局记录最优解
掌握滑动窗口+哈希的组合,能高效解决子串、子数组等连续区间问题,突破Hard瓶颈

今天的内容就到这里啦,不要走开,小编持续更新中~~~~

在这里插入图片描述

Read more

Re:从零开始的 C++ 进阶篇(三)彻底搞懂 C++ 多态:虚函数、虚表与动态绑定的底层原理

Re:从零开始的 C++ 进阶篇(三)彻底搞懂 C++ 多态:虚函数、虚表与动态绑定的底层原理

◆ 博主名称: 晓此方-ZEEKLOG博客大家好,欢迎来到晓此方的博客。⭐️C++系列个人专栏: 主题曲:C++程序设计⭐️ 踏破千山志未空,拨开云雾见晴虹。 人生何必叹萧瑟,心在凌霄第一峰 0.1概要&序論 这里是此方,好久不见。 多态是 C++ 中最核心而且是最难理解的机制之一。它不仅是语法层面的特性,更牵涉到 C++ 的对象模型、对象内存布局以及多态机制的底层实现原理。本文将从底层原理出发,系统全面解析多态的真实运作机制。这里是「此方」。让我们现在开始吧! 一,多态的概念 通俗来说,多态就是多种形态。多态分为编译时多态(静态多态) 和 运行时多态(动态多态),这里我们重点讲运行时多态。 1.1编译时多态(静态多态) 编译时多态主要就是我们前面讲的 函数重载和函数模板。 它们通过传递不同类型的参数就可以调用不同的函数,通过参数不同达到多种形态。之所以叫编译时多态,是因为实参传递给形参的参数匹配是在编译时完成的,

By Ne0inhk
【STL】stack/queue 底层模拟实现与典型算法场景实践

【STL】stack/queue 底层模拟实现与典型算法场景实践

前言 STL 中 stack 与 queue 本质是容器适配器,基于基础容器封装实现特定操作逻辑。本文先介绍容器适配器及二者核心概念,再手动模拟实现,最后通过几道算法题展示其应用,助力夯实 STL 设计思想与数据结构基础。 目录  ------------容器适配器------------ 1、什么是容器适配器? 2、为啥容器配置器不支持迭代器  ---------------stack--------------- 1、stack介绍 2、stack模拟实现 问题:为啥 stack 不用提供默认成员函数? ---------------queue-------------- 1、queue介绍 2、queue模拟实现 --------------算法题-------------- 1、最小栈 2、栈的压入、弹出序列 3、逆波兰表达式求值 4、用栈实现队列 5、用队列实现栈  ------------容器适配器------------ 1、什么是容器适配器? 适配器可以理解为“

By Ne0inhk
【C++写详细总结①】从for循环到算法初步

【C++写详细总结①】从for循环到算法初步

前言 本文通过小编自身学习的进程从而总结出本文,也希望大家可以好好学习,帮助到自己 这个是萌新考场救场代码,与本文一起食用更佳 for循环计数器 for(定义计数变量;定义结束条件;每次循环所做的动作) 示例 for(int i=1;i<=10;i++) //首先定义“i”变量作为计数数组,赋初值为“1”//然后每次循环判断条件是否成立,不成立则退出//最后每循环执行条件,此示例为每循环“i”增加1 而计数器就是在for循环有了一定执行范围的基础上创建了一个数组,进行++计数 示例 #include<iostream>// 万年不变的框架usingnamespace std;intmain(){int n; cin>>n;//输入数值表示从1~n中有几个数字int

By Ne0inhk
【C++笔记】模板初阶

【C++笔记】模板初阶

前言:         C++模板是C++中实现泛型编程的核心工具,允许程序员编写与类型无关的代码,从而提高代码的复用性和灵活性。模板在编译时进行实例化,根据实际使用的类型生成具体的代码,因此不会带来运行时开销。          一、模板基础          1.1 为什么需要模板?          在编写函数或类时,如果希望它们能处理多种数据类型(如int、double、string),传统方法是使用函数重载,但这样会产生大量重复代码或失去类型信息。 模板允许将类型作为参数,编译器根据调用时传入的具体类型生成对应的代码。          场景:需要编写一个求两个数最大值的函数,支持 int、double 和 string(按字典序)。          ①传统方法:函数重载 #include <iostream> #include <string> using namespace std; // 为 int 重载 int max(int

By Ne0inhk