209 长度最小的子数组

不遗漏:
使用滑动窗口的思路,让每一个元素分别作子数组首元素,寻找满足要求的最短子数组,慢慢加长子数组长度即可。

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

但是超时了。
后面分析发现其实就是双层循环,思路都固定了直接看题解。
正确的思路其实应该是 先移动窗口开始位置,然后移动结束位置。这样就省略更换开始位置后重新移动结束位置的过程,原理就在于当移动开始位置时的序列和肯定是大于等于要求的最短子数组,而开始位置右移就相当于减小序列和。
这里可能会有误区:如果前面的窗口把结束位置移动到了要求的子序列后,而我们又没有进行结束位置左移动,会不会遗漏。即算法只是遍历了所有的起始位置。但是没有为所有的起始位置遍历所有的结束位置。
其实是不会的,因为这种假设有一个悖论,我们假设(x+1,y-1)>t为答案,假设则要求移动得到了(x,y)>t,这隐含了(x,y-1)<t,
显然有(x+1,y-1)>t>(x,y-1),明显是矛盾的,不可能出现这种情况。
因此并不会遗漏可能存在答案的结束位置。

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

要注意:1、窗口用来保存什么;2、窗口的起始位置和结束位置在什么时候开始移动;3、窗口移动时记得更新保存内容

					904 水果成篮

实质上就是找只含两个数字的连续子序列的长度,这题的额外难度在于设计窗口的同时怎么确认这个序列中只含两个数字并同时维护数目。
1、窗口中保存的是子序列;2、当子序列中的数目种类<2时右移结束位置,>2时左移动开始位置;
思路很简单,结果发现有点无从下手...
开始是想着用两个长为2的数组来保存两个篮子的数据,发现要处理的数据还有边界判定有点多:要保存水果数目,保存种类...硬写了一下只能通过一些。
看题解发现数组解法可以用左右两个位置指针的差来计算水果数目,然后开一个长度为n的vector来保存篮子。
还有就是可以用哈希表,这样会更简单,确实没想到这一点。

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        int n = fruits.size();
        int result = 0;
        int types = 0;
        vector<int> tree(n, 0);

        for (int left = 0, right = 0; right < n; right++) {
              tree[fruits[right]]++;//比起原来的思路的好处在于避免了复杂的维护,我们只关心窗口内的数据
            if (tree[fruits[right]] == 1) {
                types++;
            }
            while (types > 2) {
                tree[fruits[left]]--;
                if (tree[fruits[left]] == 0) {
                    types--;
                }
                left++;
            }
            result = result > right - left + 1 ? result : right - left + 1;
        }
        return result;
    }
};
                  76最小覆盖子串

思路也还是比较简单的,从左边开始递增右指针直到窗口中包含t字符串中的所有字母。此时右移左指针。要注意题目要求对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。

posted @ 2024-11-01 15:45  名字好难想zzz  阅读(13)  评论(0)    收藏  举报