思路
第一时间想到的思路就是:找到一个这样的最长区间:区间内最多只有两个不同的数(如果数组中只有一个数的情况)
典型的滑动窗口问题。
多指针
沿用了昨天周赛T4的思路:6207. 统计定界子数组的数目
类似滑窗?
通过枚举右边界r
,记录左边界下标l
,额外利用几个变量来记录当前区间中存在哪两个数,并且记录其最后出现的下标位置:
x, y
:区间中的两个数lastX, lastY
:x, y
最后出现的下标,用于更新区间左边界。
算法如下:
- 初始化
x = y = lastX = lastY = -1
,l = 0
,ans = 0
; - 枚举右边界下标,每轮固定为
r
:- 若
x == -1
,说明第一个数还没有确定, 确定第一个数;若fruits[r] != x && y == -1
,即可以确定第二个数。 - 若
fruits[r] == x
,更新lastX = r
;若fruit[r] == y
,更新lastY = r
;否则进入下一步 - 此时有:
fruits[r] != x && fruits[r] != y
,说明遇到了第三个数,因此需要根据前面区间的信息对答案以及变量进行一次更新操作:- 更新答案
ans
:由上面的步骤可知区间[l, r)
内只存在两个数,ans = max(ans, r - l)
- 更新区间左边界
l
:l = min(lastX, lastY) + 1
; - 更新第二个数
x, lastX
或y, lastY
:若lastX < lastY
,则x = fruits[r]
,lastX = r
;否则y = fruits[r]
,lastY = r
。
- 更新答案
- 若
算法图示如下:
代码如下:
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int n = fruits.size();
if(n <= 2){
return n;
}
int x = -1, y = -1;
int lastX=-1, lastY=-1;
int l=0; // 记录一个起点
int ans = 0;
for(int r=0; r<n; r++) {
if(x == -1){
x = fruits[r];
}
if(fruits[r] != x && y == -1){
y = fruits[r];
}
if(x == fruits[r]){
lastX = r;
} else if(y == fruits[r]){
lastY = r;
} else {
//遇到两个数之外的数了,更新答案;
// ans = max(ans, max(lastX, lastY) - start + 1);
ans = max(ans, r - l);
//下一次的起点
if(lastX < lastY){
l = lastX + 1;
x = fruits[r];
lastX = r;
} else {
l = lastY + 1;
y = fruits[r];
lastY = r;
}
}
}
//区间遍历结束后,更新一次答案
ans = max(ans, n - l);
return ans;
}
};
滑动窗口
实际上在前面一节中的方法与滑动窗口的思路是类似的。
滑动窗口的方法一样是枚举右边界right
,记录左边界left
。
额外利用map
来记录区间[left, right]
中不同类型水果的数目。若map.size() > 2
,说明此时区间中存在了三类水果,左边界left
右移,同时对应的水果种类fruits[left]
数量减1,直至有一个水果数目被减至0,将其从map
中删除,此时得到了一个合法的区间, 更新ans
即可。
代码如下:
class Solution {
public:
//找到一个最长的连续区间:区间内只有两个数
int totalFruit(vector<int>& fruits) {
map<int, int> map;
int n = fruits.size();
int ans = 0;
int left = 0;//初始左边界
//枚举右边界
for(int right = 0; right<n; right++){
map[fruits[right]]++;
while(map.size() > 2){
if (--map[fruits[left]] == 0){
map.erase(fruits[left]);
}
left++;
}
ans = max(ans, right - left + 1);
}
return ans;
}
};