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循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置.

posted @ 2022-03-25 20:04  ggaoda  阅读(3)  评论(0编辑  收藏  举报  来源