leetcode-76. 最小覆盖子串
双指针法之滑动窗口法
题目详情
给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
注意:
- 对于
t
中重复字符,我们寻找的子字符串中该字符数量必须不少于t
中该字符数量。 - 如果
s
中存在这样的子串,我们保证它是唯一的答案。
示例1:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
示例2:
输入:s = "a", t = "a"
输出:"a"
示例3:
输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。
我的代码:
C++
class Solution
{
public:
string minWindow(string S, string T)
{
vector<int> chars(128,0); //chars映射表示当前每个字符缺少的个数
vector<bool> flag(128,false); //flag映射表示每个字符是否在t中存在
for(int i = 0;i<T.size();++i) //统计好T的情况
{
flag[T[i]]=true;
++chars[T[i]];
}
int cnt=0,l=0,min_l=0,min_size=S.size()+1;
for(int r=0;r<S.size();++r) //用r滑动窗口遍历S
{
if(flag[S[r]]) //在s中找到一个t中存在的字符
{
if(--chars[S[r]]>=0) //还没找够,就将其统计进去
{
++cnt; //cnt统计的是窗口中包含T中的总字母数
}
while(cnt==T.size()) //如果找够了所有字符
{
if(r-l+1<min_size) //如果当前在s中找到包含t中所有字符的区间比min_size更小
//就将最小区间更新为当前区间
{
min_l=l;
min_size=r-l+1;
}
//因为我们要移动左端点l继续寻找
//所以如果l的字符是t中的一个且移除后该字母的缺少数量要+1
//如果加1后缺失数量就大于0了,那么--cnt来继续寻找
if(flag[S[l]]&&++chars[S[l]]>0)
{
--cnt;
}
++l; //更新左界
}
}
}
return min_size>S.size()?"":S.substr(min_l,min_size);
//当min_size未更新过的时候会大于S.size(),就是cnt==T.size()永不成立
}
};
Java
class Solution {
public String minWindow(String s, String t) {
//chars映射表示当前每个字符缺少的个数
//flag映射表示每个字符是否在t中存在
int[] chars = new int[128];
boolean[] flag = new boolean[128];
//统计一遍t中的字符
for (int i = 0; i < t.length(); ++i){
flag[t.charAt(i)] = true; //是否存在
++chars[t.charAt(i)]; //统计数量
}
int cnt = 0, l = 0, min_l = 0, min_size = s.length() + 1;
//滑动窗口右端r
for (int r = 0; r < s.length(); ++r){
//在s中找到一个t中存在的字符
if (flag[s.charAt(r)] == true){
//还没找够,就将其统计进去
if (--chars[s.charAt(r)] >= 0) ++cnt;
//如果找够了所有字符
while (cnt == t.length()){
//如果当前在s中找到包含t中所有字符的区间比min_size更小
//就将最小区间更新为当前区间
if (r-l+1 < min_size){
min_l = l;
min_size = r - l + 1;
}
//因为我们要移动左端点l继续寻找
//所以如果l的字符是t中的一个且移除后该字母的缺少数量要+1
//如果加1后缺失数量就大于0了,那么--cnt来继续寻找
if (flag[s.charAt(l)]==true && ++chars[s.charAt(l)]>0) --cnt;
//l右移
++l;
}
}
}
return min_size > s.length()?"":s.substring(min_l,min_l+min_size);
}
}
涉及知识点:
1.双指针算法
双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。也可以延伸到多个数组的多个指针。
若两个指针指向同一数组,遍历方向相同且不会相交,则也称为滑动窗口(两个指针包围的区域即为当前的窗口),经常用于区间搜索。
若两个指针指向同一数组,但是遍历方向相反,则可以用来进行搜索,待搜索的数组往往是排好序的。
2.滑动窗口算法
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。
那么滑动窗口如何用一个for循环来完成这个操作呢。
首先要思考 如果用一个for循环,那么应该表示 滑动窗口的起始位置,还是终止位置。
如果只用一个for循环来表示 滑动窗口的起始位置,那么如何遍历剩下的终止位置?
此时难免再次陷入 暴力解法的怪圈。
所以 只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!