求最长的只含两种元素的连续子数组的长度(动态规划)
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;
}
};
在具体实现中,对于类型的管理、最后一段的位置的更新可能会有点绕,但这并不是本算法的重点,理清楚了基本算法,剩下的实际上只要细心点就可以理清楚的。

浙公网安备 33010602011771号