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