leetcode-滑动窗口

思路:

1)abs(i-j)<=k 使用滑动窗口限制

2)寻找重复元素使用哈希表,STL中的unordered_set可以满足这个使用

当滑动窗口向左滑动一个长度时,把末端元素加入到set中,同时把右边的上一个元素弹出

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        unordered_set<int> hash_w;
        
        for(int i = 0; i <= (int)(nums.size()) - k - 1 || i == 0; ++i) {
            if(i != 0) {
                hash_w.erase(nums[i-1]);
                if(hash_w.find(nums[i+k]) != hash_w.end()) {
                    return true;
                }
                hash_w.emplace(nums[i+k]);
            } else {
                for(int j = i; j <= i + k && j < nums.size(); ++j) {
                    if(hash_w.find(nums[j])!= hash_w.end()) {
                        return true;
                    }
                    hash_w.emplace(nums[j]);
                }
            }
        }
        return false;
    }
};

引申知识:

1. size()函数返回值类型为unsigned_int,注意如果用其进行运算最好先转换为int,不然当运算结果是负数时会出现问题

STL中unordered_set的使用:

1)查找find(), 删除erase(), 插入emplace()

2)底层实现


本题解题思路:

1)空间换时间:使用数组模拟哈希,26个元素的数组表示对26个字母计数,所以最后只需要比较两个数组是否相等即可

2)滑动窗口减少时间消耗的关键: 每次统计减少最左的,增加最右的,这样不用每次都对窗口内每个元素进行计数。

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> p_num(26,0);
        vector<int> s_num(26,0);
        vector<int> res;
        if (s.length() < p.length()) {
            return res;
        }
        for (int i = 0; i < p.length(); ++i) {
            ++p_num[p[i]-'a'];
            ++s_num[s[i]-'a'];
        }
        if (p_num==s_num) {
            res.push_back(0);
        }
        for (int i = 1; i <= s.length()-p.length(); ++i) {
            --s_num[s[i-1]-'a'];
            ++s_num[s[i+p.length()-1]-'a'];
            if (p_num==s_num) {
                res.push_back(i);
            }
        }
        return res;
    }
};

类型:变长窗口,固定计数,找最长子数组

比起上面两道简单的滑动窗口计数问题,这个稍微复杂了一点,但实际上仍然可以理解为滑动窗口计数问题。

理解题目:

1)关键1,计数:可以替换K个0,其实可以理解为滑动窗口内部可以最多可以有k个0

2)关键2,窗口长度:这也是本题比起上两题的难点,窗口长度是不确定的,而且是需要求出最长的窗口长度

解题思路:

仍然使用滑动窗口标志的解题方法,每次滑动都是减去左边的,增加右边的,但是由于窗口长度不确定,就不能使用单纯的滑动,要在每次滑动(左边位置加一)的同时,统计出最长的长度,一开始想过通过控制长度变化来遍历,但是这种方法耗时必定不能通过,然后联想下这种左边和右边位置都在滑动的形式,很容易想到双指针,关键:

1)左边位置滑动的时候,判断去掉的是否为0,如果是,则计数减1

2)每个左边位置下可以有一个右边位置的循环,找出当前的最大长度

class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int zero_count = 0;
        int curr_max_len = 0;
        int left_index = 0;
        int right_index = 0;
        while (left_index <= nums.size() - curr_max_len && left_index < nums.size()) {
            while (zero_count <= k && right_index < nums.size()) {
                if (!nums[right_index]) {
                    ++zero_count;
                }
                ++right_index;
            }
            int len = 0;
            len = zero_count == k+1 ?  right_index - left_index - 1 : right_index - left_index;
            if (len > curr_max_len) {
                curr_max_len = len;
            }
            if (!nums[left_index]) {
                --zero_count;
            }
            ++left_index;
        }
        return curr_max_len;
    }
};


这个其实挺简单的,但是我用了很笨的方法,代码质量也一般,晚点学习一下其他解法。

解答思路:

1)滑动窗口,但是这题的特殊点就是他有两个窗口,长度分别是firstLen和secondLen,我这里非常笨地使用了遍历各种窗口情况的解法,只是在算每个窗口内元素和的时候没有使用遍历而是使用了滑动窗口常用的左删右增方法来减少时间消耗。

2)最外层是firstLen的窗口滑动控制,内层是在这个前提下secondLen的窗口滑动控制(有两种情况,在左边或在右边)

代码:

class Solution {
public:
    int maxSumTwoNoOverlap(vector<int>& nums, int firstLen, int secondLen) {
        int firstSum = 0;
        int secondSum = 0;
        int maxSum = 0;
        for (int i = 0; i < nums.size() - firstLen + 1; ++i) {
            if (i == 0) {
                firstSum = 0;
                for (int j = i; j < firstLen; ++j) {
                    firstSum += nums[j];
                }
            } else {
                firstSum -= nums[i-1];
                firstSum += nums[i+firstLen-1];
            }
            for (int k = 0; k < i - secondLen +1; ++k) {
                if (k == 0) {
                    secondSum = 0;
                    for (int t = k; t < secondLen; ++t) {
                        secondSum += nums[t];
                    }
                } else {
                    secondSum -= nums[k-1];
                    secondSum += nums[k+secondLen-1];
                }
                if (maxSum < firstSum + secondSum) {
                    maxSum = firstSum + secondSum;
                    cout << "f index: " << i << " s index: " << k << " fsum: " << firstSum << " ssum: " << secondSum << endl;
                }
            }

    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值