C++算法题 - 滑动窗口

209. 长度最小的子数组

LeetCode_link


给定一个含有 n 个正整数的数组和一个正整数 target

找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [ n u m s l , n u m s l + 1 , . . . , n u m s r − 1 , n u m s r ] [nums_l, nums_{l+1}, ..., nums_{r-1}, nums_r] [numsl,numsl+1,...,numsr1,numsr],并返回其长度。如果不存在符合条件的子数组,返回 0

示例 1
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2
输入:target = 4, nums = [1,4,4]
输出:1

示例 3
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

提示
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105


方法:滑动窗口
思路:在每次while循环开始时,保持 s u m < t a r g e t sum < target sum<target s u m sum sum 保持 i i i j − 1 j -1 j1 的和,但其实此时 j = j j=j j=j(有点废话)。每当 s u m + n u m s [ j ] ≥ t a r g e t sum + nums[j] \geq target sum+nums[j]target 时,就是满足条件了,就要统计子数组的长度,但同时也要让 s u m sum sum i , j i, j i,j 再次不满足条件,也就是让 i i i 向后走到不满足为止,成为 s u m < t a r g e t sum < target sum<target,为下一次while循环做准备。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size();
        if(n == 0)  return 0;
        int sum = 0;
        int i = 0, j = 0;
        int minlong = n + 1;
        while(j < n){
            sum += nums[j];
            while(sum >= target){
                minlong = min(minlong, j - i + 1);
                sum -= nums[i++];
            }
            j ++;
        }
        return minlong == n + 1 ? 0 : minlong;
    }
};

3. 无重复字符的最长子串

LeetCode_link


给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc” ,所以其长度为 3。

示例 2:
输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b” ,所以其长度为 1。

示例 3:
输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke” ,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成


思路:和上题基本一致

class Solution {
private:
    unordered_map<char, int> hash;
public:
    int lengthOfLongestSubstring(string s) {
        int n = s.size();
        if(n == 0)  return 0;
        int i = 0, j = 1;
        hash[s[0]] = 1;
        int long_s = 0;
        while(j < n){
            if(hash.find(s[j]) != hash.end()){
                long_s = max(long_s, j - i);
                while(s[i] != s[j]){
                    hash.erase(s[i]);
                    i++;
                }
                i++;
            }
            hash[s[j]] = 1;
            j++;
        }
        return long_s = max(long_s, j - i);;
    }
};

30. 串联所有单词的子串

LeetCode_link


给定一个字符串 s 和一个字符串数组 wordswords 中所有字符串 长度相同

s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。

例如,如果 words = ["ab","cd","ef"], 那么 "abcdef""abefcd""cdabef""cdefab""efabcd", 和 "efcdab" 都是串联子串。 "acdbef" 不是串联子串,因为他不是任何 words 排列的连接。
返回所有串联子串在 s 中的开始索引。你可以以 任意顺序 返回答案。

示例 1
输入:s = “barfoothefoobarman”, words = [“foo”,“bar”]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 “barfoo” 开始位置是 0。它是 words 中以 [“bar”,“foo”] 顺序排列的连接。
子串 “foobar” 开始位置是 9。它是 words 中以 [“foo”,“bar”] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。

示例 2
输入:s = “wordgoodgoodgoodbestword”, words = [“word”,“good”,“best”,“word”]
输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。
s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接。
所以我们返回一个空数组。

示例 3
输入:s = “barfoofoobarthefoobarman”, words = [“bar”,“foo”,“the”]
输出:[6,9,12]
解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。
子串 “foobarthe” 开始位置是 6。它是 words 中以 [“foo”,“bar”,“the”] 顺序排列的连接。
子串 “barthefoo” 开始位置是 9。它是 words 中以 [“bar”,“the”,“foo”] 顺序排列的连接。
子串 “thefoobar” 开始位置是 12。它是 words 中以 [“the”,“foo”,“bar”] 顺序排列的连接。

提示
1 <= s.length <= 104
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i]s 由小写英文字母组成


思路
在这里插入图片描述

每步迈一个单词(因为单词等长),所以就要考虑起点在哪。如果单词长度为4,我们一步迈4个字母,那么如果起点是0,则第4个、第8个等等都会被迈到。所以起点就要考虑剩下的1,2,3情况。

用哈希表存储单词出现了几次,并用哈希表中数值的剩余量表示之后的匹配还需要多少次单词的出现。

每次匹配一整个单词,匹配上,right++,,直到不匹配了;匹配不上,left++,然后再回去看看现在right能匹配上了吗。

但是容易忽略一种情况,就是right后面的整个单词完全不在words列表中的情况。

class Solution {
private:
    unordered_map<string, int> hash;
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        int n = s.size(), m = words[0].size(), nums = words.size();
        string cut = "";
        vector<int> res;
        for(int i = 0; i < m; i ++){
            //创建hash
            hash.clear();
            init_hash(words);
            //用hash按照单词步长
            int left = i, right = i;
            while(right + m <= n){
                cut = s.substr(right, m);
                while(hash.find(cut) != hash.end()){
                    //找到
                    hash_delete(cut);
                    right += m;
                    if (right + m <= n) {
                        cut = s.substr(right, m);
                    }else{
                        break;
                    }
                }
                if(hash.empty())    res.push_back(left);

                if(left < right){
                    cut = s.substr(left, m);
                    hash_insert(cut);
                    left += m;
                }else{
                    //在接下来一个单词完全不再words中时,会进入到这里
                    right += m;
                    left = right;
                }
            }
        }
        return res;
    }
    void init_hash(vector<string>& words){
        int nums = words.size();
        for(int j = 0; j < nums; j ++){
            hash_insert(words[j]);
        }
    }
    void hash_insert(string cut){
        if(hash.find(cut) == hash.end()){
            hash[cut] = 1;
        }else{
            hash[cut] ++;
        }
    }
    void hash_delete(string cut){
        if(hash[cut] == 1){
            hash.erase(cut);
        }else{
            hash[cut] --;
        }
    }
};

76. 最小覆盖子串

LeetCode_link


给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

注意
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”
解释:最小覆盖子串 “BANC” 包含来自字符串 t 的 ‘A’、‘B’ 和 ‘C’。

示例 2
输入:s = “a”, t = “a”
输出:“a”
解释:整个字符串 s 是最小覆盖子串。

示例 3:
输入: s = “a”, t = “aa”
输出: “”
解释: t 中两个字符 ‘a’ 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

提示
m == s.length
n == t.length
1 <= m, n <= 105
st 由英文字母组成


思路:需要两个主要的哈希表,need用来统计目前的窗口中是否包含所有字符串t的字母,管理right走多远;have用来统计属于字符串t的字母,但是比其要求的数量多了多少个,比如t="ABC",当前窗口是"AABC",这时候就多了一个"A",这个用来决定left怎么走,left会跨过不属于t的以及多出来的存放在have中的字母。我用了一个哈希表flag用来判断字符是不是存在于字符串t里的。

class Solution {
private:
    unordered_map<char, int> have;
    unordered_map<char, int> need;
    unordered_set<char> flag;
public:
    string minWindow(string s, string t) {
        int n = s.size(), m = t.size();
        if(m > n)   return "";
        int minlong = n + 1;
        int left = 0, right = 0;
        int min_start = -1;
        init_hash(t);
        while(right < n && left <= right){
            while(!need.empty() && right < n){  //循环让右指针往右走
                if(flag.find(s[right]) != flag.end()){
                    if(need.find(s[right]) != need.end()){
                        delete_need(s[right]);
                    }else{
                        insert_have(s[right]);
                    }
                }
                right ++;
            }
            while(left < right && (flag.find(s[left]) == flag.end() || have.find(s[left]) != have.end())){ //跨越所有不属于的和多余的
                if(have.find(s[left]) != have.end()){
                    delete_have(s[left]);
                }
                left ++;
            }
            if(need.empty() && minlong > right - left){ // 满足条件就记录下来
                minlong = right - left;
                min_start = left;
            }
            delete_have(s[left]);//去掉满足条件的最左的字母,以便往后寻找可能存在的更小的
            insert_need(s[left]);
            left ++;
        }
        return min_start < 0 ? "" : s.substr(min_start, minlong);
    }
    void init_hash(string t){
        int m = t.size();
        for(int i = 0; i < m; i++){
            if(flag.find(t[i]) == flag.end()){
                flag.insert(t[i]);
                need[t[i]] = 1;
            }else{
                need[t[i]] ++;
            }
        }
    }
    void delete_need(char c){
        if(need[c] > 1){
            need[c] --;
        }else{
            need.erase(c);
        }
    }
    void delete_have(char c){
        if(have.find(c) != have.end()){
            if(have[c] > 1){
                have[c] --;
            }else{
                have.erase(c);
            }
        }
    }
    void insert_have(char c){
        if(have.find(c) == have.end()){
            have[c] = 1;
        }else{
            have[c] ++;
        }
    }
    void insert_need(char c){
        if(need.find(c) == need.end()){
            need[c] = 1;
        }else{
            need[c] ++;
        }
    }
};
  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

x_fengmo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值