Leetcode 76. 最小覆盖子串(困难) 567. 字符串的排列(中等) 438. 找到字符串中所有字母异位词(中等) 3. 无重复字符的最长子串(中等) 滑动窗口算法

labuladong讲解

76. 最小覆盖子串(困难)

题目:

 

 

思路:

在s中找出包含t所有字母的最小子串。

使用滑动窗口算法,在need哈希表中保存t所有字母的次数。

先扩展right,如果s[right]在need中存在,就增加window[c]次数,如果window[c]==need[c],则valid++

当valid>=need.size时,判断子串大小是否需要存储,然后移动left,如果s[left]在need中存在,如果window[d]==need[d],valid--

class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char,int> need,window;
        int valid=0;
        // 记录最小覆盖子串的起始索引及长度
        int len=INT_MAX;
        int start=0;
        int left=0,right=0;
        for(int i=0;i<t.size();++i){
            need[t[i]]++;
        }
        while(right<s.size()){
            // c 是将移入窗口的字符
            char c=s[right];
            // 右移窗口
            right++;
            // 进行窗口内数据的一系列更新
            if(need.count(c)>0){
                window[c]++;
                if(window[c]==need[c]){
                    valid++;
                }
            }
            // 判断左侧窗口是否要收缩
            while(valid==need.size()){
                // 在这里更新最小覆盖子串
                if(right-left<len){
                    len=right-left;
                    start=left;
                }
                // d 是将移出窗口的字符
                char d=s[left];
                // 左移窗口
                left++;
                // 进行窗口内数据的一系列更新
                if(need.count(d)>0){
                    if(window[d]==need[d]){
                        valid--;
                    }
                    window[d]--;
                }
            }
        }
        // 返回最小覆盖子串
        string ret;
        if(len<INT_MAX){
            ret=s.substr(start,len);
        }
        return ret;
    }
};

 

567. 字符串的排列(中等)

题目:

 

 

思路:

相当给你一个 S 和一个 T,请问你 S 中是否存在一个子串,包含 T 中所有字符且不包含其他字符

对于这道题的解法代码,基本上和最小覆盖子串一模一样,只需要改变两个地方:

1、本题移动 left 缩小窗口的时机是窗口大小大于 t.size() 时,应为排列嘛,显然长度应该是一样的。

2、当发现 valid == need.size() 时,就说明窗口中就是一个合法的排列,所以立即返回 true

 

class Solution {
public:
    // 判断 s 中是否存在 t 的排列
    bool checkInclusion(string s1, string s2) {
        unordered_map<char,int> need,window;
        int left=0,right=0;
        int valid=0;
        for(int i=0;i<s1.size();++i){
            need[s1[i]]++;
        }
        while(right<s2.size()){
            char c=s2[right];
            right++;
            // 进行窗口内数据的一系列更新
            if(need.count(c)>0){
                window[c]++;
                if(window[c]==need[c])
                    valid++;
            }
            // 判断左侧窗口是否要收缩
            while(right-left>=s1.size()){
                // 在这里判断是否找到了合法的子串
                if(valid==need.size()){
                    return true;
                }
                // 进行窗口内数据的一系列更新
                char d=s2[left];
                left++;
                if(need.count(d)>0){
                    if(window[d]==need[d])
                        valid--;
                    window[d]--;
                }
            }
        }
        // 未找到符合条件的子串
        return false;
    }
};

 

438. 找到字符串中所有字母异位词(中等)

题目:

 

 

思路:

相当于,输入一个串 S,一个串 T,找到 S 中所有 T 的排列,返回它们的起始索引。

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        unordered_map<char,int> need,window;
        int left=0,right=0;
        // 记录结果
        vector<int> ret;
        int valid=0;
        for(int i=0;i<p.size();++i){
            need[p[i]]++;
        }
        while(right<s.size()){
            char c=s[right];
            right++;
            // 进行窗口内数据的一系列更新
            if(need.count(c)>0){
                window[c]++;
                if(window[c]==need[c])
                    valid++;
            }
            // 判断左侧窗口是否要收缩
            while(right-left>=p.size()){
                // 当窗口符合条件时,把起始索引加入 res
                if(valid==need.size()){
                    ret.push_back(left);
                }
                char d=s[left];
                left++;
                // 进行窗口内数据的一系列更新
                if(need.count(d)>0){
                    if(window[d]==need[d])
                        valid--;
                    window[d]--;
                }
            }
        }
        return ret;
    }
};

 

3. 无重复字符的最长子串(中等)

题目:

 

 

思路:

连 need 和 valid 都不需要,而且更新窗口内数据也只需要简单的更新计数器 window 即可。

当 window[c] 值大于 1 时,说明窗口中存在重复字符,不符合条件,就该移动 left 缩小窗口

需要注意的是,要在收缩窗口完成后更新 res,因为窗口收缩的 while 条件是存在重复元素,换句话说收缩完成后一定保证窗口中没有重复嘛。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char,int> window;
        int left=0,right=0;
        int ret=0;
        while(right<s.size()){
            char c=s[right];
            right++;
            // 进行窗口内数据的一系列更新
            window[c]++;
            // 判断左侧窗口是否要收缩
            while(window[c]>1){
                char d=s[left];
                left++;
                // 进行窗口内数据的一系列更新
                window[d]--;
            }
             // 在这里更新答案
            ret=max(ret,right-left);
        }
        return ret;
    }
};

 

posted @ 2022-02-26 22:13  鸭子船长  阅读(32)  评论(0编辑  收藏  举报