代码随想录学习记录——数组篇-长度最小的子数组

145 阅读1分钟

209、长度最小的子数组

本题我尝试了两层循环去解决问题,但是超时了

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int minLen = nums.size() + 1;
        for(int i = 0; i < nums.size();i++){
            int sumCur = nums[i];
            int lenCur = 1;
            int j = i+1;
            while(sumCur < target && j < nums.size()){
                sumCur += nums[j];
                j++;
                lenCur++;
            }

            if(sumCur >= target){
                if(lenCur < minLen ){
                    minLen = lenCur;
                }
            }else{
                continue;
            }
        }
        if(minLen <= nums.size()){
            return minLen;
        }else{
            return 0;
        }
    }
};

观看了代码随想录的讲解,了解到了滑动窗口的概念,这里的关键思路便是:

  • 如果当前窗口内总和小于targettarget,那么终止位置就需要往后移动,纳入新的值
  • 如果当前窗口内总和大于targettarget,那么就需要判断是否是最小长度,同时移动起始位置
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int result = INT32_MAX;
        int sum = 0;
        int i = 0;
        int subLength = 0;
        for( int j = 0; j < nums.size(); j++){
            sum += nums[j];
            while(sum >= target){
                subLength = (j - i + 1);
                if(subLength < result){
                    result = subLength;
                }
                sum -= nums[i++];
            }
        }
        if(result == INT32_MAX){
            return 0;
        }else{
            return result;
        }
    }
};

904、水果成篮

这一题的滑动窗口我在更新的时候一直理不清方法,因此也是参考了大佬的代码,用了哈希表来实现:

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        unordered_map<int, int> basket;
        int maxLen = 0;
        int curLen = 0;
        int startIndex = 0;
        for(int i = 0;i<fruits.size();i++){
            basket[fruits[i]]++; // 如果里面有就++,没有创建并++
            curLen++;
            while(basket.size() > 2){  //如果当前有的大于2了,那么要缩减
                basket[fruits[startIndex]]--; //减去当前起始位置的
                if(basket[fruits[startIndex]] == 0){  //如果该类等于0就可以删除了
                    basket.erase(fruits[startIndex]);
                }
                startIndex++; // 移动,如果还是大于2还要继续减
                curLen--;
            }
            if(   maxLen < curLen ){
                maxLen = curLen;
            }
        }
        return maxLen;
    }
};

76、最小覆盖字串

该题可以用滑动窗口解决,但是难点在于如果判断当前窗口内的子串满足条件,因此字符串tt中的字符有可能是重复的。

看了大神的思路,具体为:

  • 用一个变量cnt来记录当前滑动窗口的子串中,拥有tt中所含有字符的个数

  • 用一个哈希表记录tt中字符和对应的频数,用另一个哈希表来记录滑动窗口中的字符和对应频数

  • 用两个指针 iij j 来表示滑动窗口的前后

  • cnt的更新规则:如果 cnt<t.size()cnt < t.size() 那么说明此时滑动窗口的子串还没达到要求,那么 i++i++ ,往后延伸,只有当 winFreq[s[i]]<=tFreq[s[i]]winFreq[s[i]] <= tFreq[s[i]] 的时候才cnt++cnt++,因为这样才说明此时未满足要求仍然需要纳入目标字符;只有当winFreq[s[j]]>tFreq[s[j]]winFreq[s[j]] > tFreq[s[j]] 时,才可以将末尾的字符丢弃,这样仍然能够满足该字符的要求,即对cntcnt不做修改。重点就是只要当前的子串中目标字符的个数仍然没够,我们就纳入目标字符并增加cnt,直到子串满足了(即cnt==t.size()cnt==t.size() ),那么就判断是否是最小,然后将窗口末尾开始收缩。

class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char, int> winFreq;
        unordered_map<char, int> tFreq;
        for(auto c:t){
            tFreq[c]++;
        }
        int cnt = 0;
        string result;
        for( int i = 0,j = 0; i < s.size(); i++){
            winFreq[s[i]]++;
            if( winFreq[s[i]] <= tFreq[s[i]]){
                cnt++;
            }
            while(winFreq[s[j]] > tFreq[s[j]]){
                winFreq[s[j++]]--;
            }
            if(cnt == t.size()){
                if(result.empty() || i-j+1 < result.size()){
                    result = s.substr(j,i-j+1);
                }
            }
        }
        return result;
    }
};