分析:这个问题本质上是求解 包含两个不同元素的最长连续子序列 问题,由于是 连续子序列 ,因此可以考虑采用滑动窗口的方法来求解
- 第一类水果初次出现的位置为左窗口,当前判断的元素位置为右窗口
- 如果出现第三类水果,则之前两类水果有一类需要为新的水果类型腾出位置
- 距离当前右窗口最近的连续元素的首位所代表的水果类型为保留的水果类型
- 需要一个变量 f1 表示滑动窗口的左边界,也就是第一类水果首次出现的位置
- 需要一个变量 f1-last 表示第一类水果最后一段连续出现的首位
- 需要一个变量 f2-last 同时表示第二类水果的类型 和 第二类水果最后一段连续出现的首位
- 距离当前右窗口最近的连续元素的首位所代表的水果类型为保留的水果类型
- 如果出现的水果类型不是新水果,则判断是否需要对 f2-last 和 f1-last 进行更新
- 当前水果类型与前一个元素的水果类型一致,说明水果类型仍然连续,最后一段首次出现的位置不需要更新
- 当前水果类型与前一个元素的水果类型不一致,则需要进行更新。当前位置的水果类型为该类型水果最后一段连续出现的首位
我的做法:
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int result = 0;
//f1表示滑动窗口的左边界,也就是第一类水果首次出现的位置
//f1-last和f2-last分别表示第一类和第二类水果最后一段连续出现的首位
//这里的f2-last既承担种类,也表示最后一段连续出现的首位
int f1 = 0, f1_last = 0,f2_last = 0;
int len = 0;
for(int j = 0; j < fruits.size(); j++){
//新来的水果既不属于第一类,也不属于第二类
if(fruits[j] != fruits[f1] && fruits[j] != fruits[f2_last]){
if(f1 == f2_last){ //f1==f2,则第二个篮子还没有装水果
f2_last = j;
}
else{
//选择距离j最近的水果作为第一个篮子的种类
f1 = f1_last > f2_last ? f1_last : f2_last;
//如果选择f2-last,由于f2-last本来就代表最后一段连续的首位,所以f1-last要更新为f2-last
f1_last = f1 == f2_last ? f2_last : f1_last;
f2_last = j; //新来的第三类水果放在第二个篮子里
}
}
else{ //如果新来的的水果与两个篮子里水果的种类相同,则需考虑是否需要更新f2-last和f1-last
if(j>0 && fruits[j-1] != fruits[j]){
f1_last = fruits[j] == fruits[f1] ? j :f1_last;
f2_last = fruits[j] == fruits[f2_last] ? j : f2_last; }
}
len = j - f1 + 1;
result = result > len ? result : len; //每次for的循环都会进行长度的计算
}
return result;
}
};
进阶级:
下面这种为进阶型,比较难理解。大概讲就是用 f1 和 f2 表示水果类型,用 t 表示距离当前右窗口最近的连续元素的首位,水果类型变化,则更新t
class Solution {
public:
int totalFruit(vector<int>& fruits) {
//当fruits数组长度≤2,则直接返回fruits数组长度
if(fruits.size()<3) return fruits.size();
//mn为结果,即fruits数组中包含两个不同水果类型的最大子序列长度
int mn=0;
/*j为遍历fruits数组的指针,也是滑动窗口的右边界,f1,f2分别为第一个和第二个篮子
的起始索引,f1同时也是滑动窗口的左边界,t为未来两个篮子里的第一个篮子的起始索
引*/
for(int j=0,f1=0,f2=0,t=0;j<fruits.size();j++)
{
/*当f1=f2时,fruits[j]!=fruits[f1]&&fruits[j]!=fruits[f2]说明遇到的是第二个篮子
要装的第二种水果;当f1!=f2时,fruits[j]!=fruits[f1]&&fruits[j]!=fruits[f2]说明
遇到的是第三种水果*/
if(fruits[j]!=fruits[f1]&&fruits[j]!=fruits[f2])
{
/*当f1=f2时,说明第一个篮子已经装了一种水果,第二个篮子里还没有装,不更新f1,
只更新f2,即往第二个篮子里装第二种水果;
当f1!=f2时,fruits[j]!=fruits[f1]&&fruits[j]!=fruits[f2]说明遇到的是第三种水果,
则更新f1和f2,即当前两个篮子的两种水果已装满,更新f1和f2为未来两个篮子里的两种
水果*/
if(f1!=f2) f1=t;
f2=j;
}
/*t寻找未来两个篮子中第一个篮子要装的一种水果的起始索引,每次t与当前的j作对比,
当fruits[t]!=fruits[j]时,更新t=j*/
if(fruits[t]!=fruits[j]) t=j;
/*更新当前子序列的长度,即每次计算装入两个篮子中的水果总数目*/
mn=mn>(j-f1+1)?mn:(j-f1+1);
}
return mn;//返回最终结果mn
}
};