LeetcodeHot100|76.最小覆盖子串
76.最小覆盖子串
给你一个字符串s,字符串t. 返回s中涵盖t所有字符的最小子串。如果s中不存在涵盖t所有字符的子串,则返回空字符串""。
遇到的问题
-
找出所有符合条件的字段,然后统计长度。统计abc字符出现的次数,这种方法的问题是?
-
如何统计各个字符出现的次数?

思路分析
这种题目一般使用滑动窗口来实现这个问题,在这种问题中一般会有两个指针,一个指针r用于窗口的扩展
,一个l用于收缩窗口的l指针。在任意时刻,一个指针运动,另一个保持禁止。所有回到题目,在字符s上不断移动,通过指针r不断扩张窗口,当包含t全部的字符之后,如果能收缩就收缩到不能收缩为止,也就是得到最小窗口。
题目解答
#include <unordered_map>
#include <climits>
#include <string>
using namespace std;class Solution {
public:// ori:存储目标字符串t中各字符的出现次数(基准哈希表)// cnt:存储当前滑动窗口中各字符的出现次数(动态哈希表)unordered_map<char, int> ori, cnt;// 检查当前滑动窗口是否包含目标字符串t的所有字符,且各字符数量不小于t中的数量bool check() { // 遍历目标哈希表ori中的每个字符for (const auto &p : ori) { // 如果当前窗口中该字符的数量小于目标数量,说明窗口不满足条件if (cnt[p.first] < p.second) { return false;}}// 所有字符都满足数量要求,窗口有效return true; }// 寻找s中包含t所有字符的最小长度子串string minWindow(string s, string t) {// 初始化目标哈希表ori:统计t中每个字符的出现次数for (const auto &c : t) { ++ori[c];}// l:滑动窗口左边界,r:滑动窗口右边界(初始化为-1,方便首次++r指向0)int l = 0, r = -1; // len:当前找到的最小窗口长度(初始化为最大值)// ansL:最小窗口的起始索引,ansR:最小窗口的结束索引(初始化为-1表示未找到)int len = INT_MAX, ansL = -1, ansR = -1; // 右指针遍历整个字符串s(当r小于s长度时持续循环)while (r < int(s.size())) { // 右指针右移一位(首次循环会从-1移到0)// 如果当前字符是t中包含的字符(存在于ori中),则在cnt中增加其计数if (ori.find(s[++r]) != ori.end()) { ++cnt[s[r]];}// 当窗口满足条件(包含t所有字符),且左指针不超过右指针时,尝试收缩左边界以寻找更小窗口while (check() && l <= r) { // 计算当前窗口长度,若小于已知最小长度,则更新最小长度和窗口索引if (r - l + 1 < len) { len = r - l + 1;ansL = l; // 记录当前窗口的起始位置}// 如果左边界字符是t中包含的字符,移除它时需要减少cnt中的计数if (ori.find(s[l]) != ori.end()) { --cnt[s[l]];}// 左指针右移,收缩窗口++l;}}// 如果未找到有效窗口(ansL仍为-1),返回空字符串;否则返回最小窗口子串return ansL == -1 ? string() : s.substr(ansL, len);}
};
看完题解后
- for (const auto &p : ori) {这里使用const 以及&的目标是?可以不用吗
- 避免拷贝:
ori是一个unordered_map<char, int>,其中的元素是pair<char, int>类型。如果不用引用(即写成for (auto p : ori)),每次循环都会将ori中的元素拷贝一份到p中。对于哈希表中的每个元素,拷贝操作会额外消耗时间(尤其是当哈希表很大时,性能影响更明显)。 - 直接访问原元素:使用引用
&p可以直接访问ori中的元素,无需拷贝,提高循环效率。
- 避免拷贝:
