最小字符串覆盖 and 找到字符串中所有字母异位词and无重复字符的最长子串(都是滑动窗口方法的应用)

经典滑动窗口问题
滑动窗口算法的思路是这样:

1、我们在字符串 S 中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引闭区间 [left, right] 称为一个「窗口」。

2、我们先不断地增加 right 指针扩大窗口 [left, right],直到窗口中的字符串符合要求(包含了 T 中的所有字符)。

3、此时,我们停止增加 right,转而不断增加 left 指针缩小窗口 [left, right],直到窗口中的字符串不再符合要求(不包含 T 中的所有字符了)。同时,每次增加 left,我们都要更新一轮结果。

4、重复第 2 和第 3 步,直到 right 到达字符串 S 的尽头。

这个思路其实也不难,第 2 步相当于在寻找一个「可行解」,然后第 3 步在优化这个「可行解」,最终找到最优解。左右指针轮流前进,窗口大小增增减减,窗口不断向右滑动。

可以用两个哈希表当作计数器解决。用一个哈希表 needs 记录字符串 t 中包含的字符及出现次数,用另一个哈希表 window 记录当前「窗口」中包含的字符及出现的次数,如果 window 包含所有 needs 中的键,且这些键对应的值都大于等于 needs 中的值,那么就可以知道当前「窗口」符合要求了,可以开始移动 left 指针了。
C++
一、最小字符串覆盖
题目描述
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。

示例:

输入: S = “ADOBECODEBANC”, T = “ABC”
输出: “BANC”
说明:

如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。

class Solution {
public:
    string minWindow(string s, string t) {
        int minLen = INT_MAX;
        int start = 0;
        unordered_map<char,int>windows;//定义哈希表,把它做成一个字典;
        unordered_map<char,int>needs;//定义哈希表,把它做成一个字典;
        for(char c:t){
            needs[c]++;//初始化一个t字典;
        }
        int len=s.size();
        int left=0,right=0;
        int match=0;
        //这时,要做的是移动right,将windows窗口内包含t中所有的字符;
        while(right<len){
            char c1=s[right];
            if(needs.count(c1)){
                windows[c1]++;
                //windows中的c1键值等于needs中的c1键值时,匹配数加一;
                if(windows[c1]==needs[c1]){
                    match++;
                }
            }
            right++;
        
            //这时,windows里面已经包含了所有的t中的字符,接下来要做的是移动
            //left去优化windows窗口;
            while(match==needs.size()){
                //这里是判断是否达到最后的windows中字符长度;
                //这是求最小子串的判断条件
                if(right-left<minLen){
                    minLen=right-left;
                    start = left;
                }
                char c2 = s[left];
                //如果c2这个字符在needs中,
                //则将windows中c2的键值--;
                if(needs.count(c2)){
                    windows[c2]--;
                    //当windows中的c2的键值小于needs(即t中的字符数)中键值,
                    //则匹配数(match)减一;
                    if(windows[c2]<needs[c2]){
                        match--;
                    }
                }
                left++;
            }
        }
        return minLen==INT_MAX?"":s.substr(start,minLen);
    }
};

二、
这题的关键是判断条件是由原来的求最小子串,改为了求子串,那么直接就是求子串与目标串的大小相等
题目描述:
给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。

字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。

说明:

字母异位词指字母相同,但排列不同的字符串。
不考虑答案输出的顺序。
示例 1:

输入:
s: “cbaebabacd” p: “abc”

输出:
[0, 6]

解释:
起始索引等于 0 的子串是 “cba”, 它是 “abc” 的字母异位词。
起始索引等于 6 的子串是 “bac”, 它是 “abc” 的字母异位词。
示例 2:

输入:
s: “abab” p: “ab”

输出:
[0, 1, 2]

解释:
起始索引等于 0 的子串是 “ab”, 它是 “ab” 的字母异位词。
起始索引等于 1 的子串是 “ba”, 它是 “ab” 的字母异位词。
起始索引等于 2 的子串是 “ab”, 它是 “ab” 的字母异位词。

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int>res;
        int len = s.length();
        int left=0,right=0;
        unordered_map<char,int>windows;
        unordered_map<char,int>needs;
        int match=0;
        for(char c:p){
            needs[c]++;
        }
        while(right<len){
            char c1=s[right];
            if(needs.count(c1)){
                windows[c1]++;
                if(needs[c1]==windows[c1]){
                    match++;
                }
            }
            right++;
            while(match==needs.size()){
            //这里的判断条件才是最关键的
                if(right-left==p.length()){
                    res.push_back(left);
                }
                char c2 = s[left];
                if(needs.count(c2)){
                    windows[c2]--;
                    if(windows[c2]<needs[c2]){
                        match--;
                    }
                }
                left++;
            }
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值