求最长的只含两种元素的连续子数组的长度(动态规划)

LeetCode题号904 水果成篮,地址:https://leetcode-cn.com/problems/fruit-into-baskets/

题目说得挺复杂,但是实际上就是求出一个连续子数组,只含有两种元素且长度最长,主要思路来自最大连续子数组和算法(动态规划解释)

题目举例:

  输入:[3,3,3,1,2,1,1,2,3,3,4]
  输出:5
  解释:[1,2,1,1,2]

具体思路:

记原数组为A,定义和A等长的数组B,数组中的元素B[i]表示的含义为以a[i]为结束的最长的只含两种元素的连续子数组,比如:

A = [3,3,3,1,2]
B[0] = 1  => 是子数组[1        ]的长度
B[1] = 2  => 是子数组[1,3      ]的长度
B[2] = 3  => 是子数组[1,3,3    ]的长度
B[3] = 4  => 是子数组[1,3,3,1  ]的长度
B[4] = 2  => 是子数组[      1,2]的长度

  显然可以知道的是,B中的最大值,就是整个问题的解,下面探讨B[i+1]和B[i]之间的关系:

注意到每个B[i]都代表一个子数组,所以其实也可以看做是子数组的生成过程

① B中的元素种类小足2,那就直接把A[i]加入子数组,同时注意更新元素种类数量(上述例子中的B[0]、B[1])

② B中的元素种类为2,在细分两种情况:

  如果A[i+1]是B[i]中两种元素之一,那么B[i+1] = B[i] + 1(上述例子中的B[2]、B[3])

  如果A[i+1]不是B[i]中两种元素之一,那么需要保留靠后面的一种,并且只能保留最后面的那一段,以保持连续性(上述例子中的B[4])

构造完成之后输出最大值即可。和最大连续子数组和算法(动态规划解释)类似,这个算法在实现过程可以进行编程上的优化,B[i+1]只和B[i]、A[i+1]相关,最大值也可以边算边记录,子数组并不需要整个记录,具体代码如下:

class Solution {
public:
    int totalFruit(vector<int>& tree) {
        int typeCount = 0;        // 当前子数组中有多少类数字
        int type1, type2;         // 每个类的数字
        int type1Last, type2Last; // 最后一段的起点,因为替换类型的时候要保留最后一段
        int subLen = 0;           // 当前子数组长度
        int max = 0;              // 最大值
        
        for (int i = 0; i < tree.size(); i++) {
            if (typeCount == 0) {
                typeCount = 1;
                type1 = tree[i];
                type1Last = i;
                subLen++;
            } else if (typeCount == 1) {
                if (tree[i] != type1) {
                    type2 = tree[i];
                    type2Last = i;
                    typeCount = 2;
                }
                subLen++;
            } else {
                if (tree[i] != type1 && tree[i] != type2) {
                    // 只保留最后一段
                    if (type1Last > type2Last) {
                        type2 = tree[i];
                        type2Last = i;
                        subLen = i - type1Last + 1;
                    } else {
                        type1 = tree[i];
                        type1Last = i;
                        subLen = i - type2Last + 1;
                    }
                } else {
                    // 更新最后一段的位置
                    if (tree[i] == type1 && type1Last < type2Last)
                        type1Last = i;
                    else if (tree[i] == type2 && type2Last < type1Last)
                        type2Last = i;
                    subLen++;
                }
            }
            
            max = max > subLen ? max : subLen;
        }
        
        return max;        
    }
};

  在具体实现中,对于类型的管理、最后一段的位置的更新可能会有点绕,但这并不是本算法的重点,理清楚了基本算法,剩下的实际上只要细心点就可以理清楚的。

原文地址:https://www.cnblogs.com/guobaoxu/p/10850582.html