06、限定条件下的移动窗口

前提:

对于一般下的移动窗口问题,就是给定一个定义好的长度target, 然后再子数组找到最小长度满足里面的元素之和是大于target就可以了。

但是对于限定条件的移动窗口问题,就需要改变策略了,例如:

题目:

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果种类 。

你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

  • 你只有两个篮子,并且每个篮子只能装单一类型的水果。每个篮子能够装的水果总量没有限制。
  • 你可以选择任意一棵树开始采摘,你必须从每棵树(包括开始采摘的树)上恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
  • 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
    给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。

简化这个题目:再一个数组fruits中,fruits[i]表示的是在第i 棵树上的水果类型,这个类型是用数字来表示的,现在有两个篮子,一个篮子装一个类型的水果,一个树只能采摘一次水果,然后往后面开始采摘,求可以找到的最大子数组长度

源代码:

class Solution{

public:
  int totalFruit(vector<int> &fruits){
    
    const int MAX = 100001;  // 定义最大数
    int i = 0; // 定义起始位置
    int kind = 0; // 定义种类数,不可以大于2
    int result = 0; // 需要返回的数
    vector<int> freq (MAX,0);   // 一个用来存储当前窗口种类的数量,下标表示种类,其对应的为数量

    for (int j = 0 ; j <fruits.size() ; j++){  // j 负责窗口的终止位置
      int fruit = fruits[j];
      freq[fruit] ++;   // 开始统计种类fruit对应的数量
      
      if (freq[fruit] == 1){
           // 开始判断是否有数量,如果有就将kind ++;
          kind ++;
      }

      while (kind > 2){
        int leftfruit = fruit[i];  //如果种类超过了2就要将 i 前移,但是你得把存放数量的置零

        // 下面的部分就是移动窗口的精髓了,移动左边起始位置
        freq[leftfruit] --;
        if (freq[leftfruit] == 0) kind --;
        
        i ++;  
      }


      // OK呀,到了这一步就要开始统计长度了
      int subLength = j - i + 1;
      result = result > subLength ? result : subLength;



    }

    return result;



  }
  
};

注:

  • 这道题多了一个判定条件就是要求kind > 2,但是本质还是一样的
  • 新增一个数组,用来存放类型的数量
  • 在kind ++ 的时候,判定条件是freq[leftfruit] == 1 ,因为开始时候的数组都是0,只要等于1了就说明有了新的类型,才加1,为而不是其他的条件,执行kind ++才保证执行了一次
  • freq[leftfruit] == 0时候 kind -- ,也同样避免了kind在变化的时候freq中的数量还没有变为0
  • freq是用来存放当前窗口的类型数量,所以本质上也是一个移动的存储器
posted @ 2026-06-05 20:15  黑单  阅读(4)  评论(0)    收藏  举报