题目描述:代码随想录-数组-5-滑动窗口
中等
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组
fruits
表示,其中fruits[i]
是第i
棵树上的水果 种类 。你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
- 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
- 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
- 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组
fruits
,返回你可以收集的水果的 最大 数目。
思路:
1、直接暴力解
类似双指针的滑动窗口,定义左指针(慢,i)和右指针(快,j),定义一个sum 是记录这次能装多少个水果,定义type1,type2记录两种水果的种类。慢指针指向装的第一个水果(顺便定义了type1 = fruits[i]),定义 k 代表第二种水果第一次出现的位置(顺便定义了type2 = fruits[k]),快指针从0开始循环,往右移动的过程中,每次移动需要判断 fruits[j] 是否等于type1或type2中的一个,如果等于,sum++ ;如果不等于,就需要重置,即挪动 i 指针,第一种方法是直接 i++,sum--, 太费时间;第二种方法是直接把 i 移到 第二种水果第一次出现的位置即 k ,此时 sum也需要更新为 sum - (k - i) 。应该就差不多了。
class Solution {
public:
int totalFruit(vector<int>& fruits) {
//符合只有两个不同数的,最长子序列,滑动窗口 15:22 (5月底没想完)
//7.2 继续想20:00开始,想到21.30也没AC 纯傻逼;
//7.4下午3.10又开始debug,暴力解应该没问题了,就是超出时间限制,o(n^2)
//7.4 想更新i-----16.44通过了,注意,滑动窗口i跳了的话,Sum也得跳
int i = 0;
int sum = 1;
int type1 = -1;
int type2 = -1;
int result = 0;
//边界情况,输入[0]
if(fruits.size() == 1) return 1;
//快指针在外循环
for (int j = 1; j < fruits.size(); j++){
//定义type1
type1 = fruits[i];
// if((fruits[j] != type1) && (type2 == -1)) type2 = fruits[j]; //初值赋的不对,这样会跳过中间某些数如 [3,3,3,1,2,1,1,2,3,3,4]
//定义type2 出现在 k处
int k = i; //k的位置也很重要,必须在这个外面
if((fruits[j] != type1) && (type2 == -1)){
for(;k<=j;k++){
if(fruits[k]!=fruits[i]){
type2 = fruits[k];
break;
}
}
}
if((fruits[j] == type1) || (fruits[j] == type2)){
sum++;
if(sum > result) {
result = sum; // result = j - i + 1; //都可以
//cout<<"result (in j = "<<j<<" ) :"<<reuslt<<endl;
}
}else{
// 重置
// way1 -- 暴力解 i每次只挪一下,way1在leetcode会超出时间
// i++;
// sum--; //一定是sum--!!!因为i往右边移的同时,Sum-1即可
//way2 -- 滑动窗口 想直接跳i
sum = sum - (k - i); //sum得在i前更新
i = k;
j--; // 一定要!!!不论way1还是way2都要保证j不动,所以先减,再下一次循环再加
type2 = -1;
}
}
return result;
}
};
白板输出:
class Solution904 {
public:
int totalFruit(vector<int>& fruits) {
//符合只有两个不同数的,最长子序列,滑动窗口 15:22 (5月底没想完)
//7.2 继续想20:00开始
int i = 0;
int sum = 1;
int type1 = -1;
int type2 = -1;
int result = 0;
int k = -1;
//边界情况,输入[0]
if(fruits.size() == 1) return 1;
//快指针在外循环
for (int j = 1; j < fruits.size(); j++){
cout<<"j: "<<j<<endl;
//定义type1
type1 = fruits[i];
//if((fruits[j] != type1)&& (type2 == -1)) type2 = fruits[j];//初值赋的不对,这样会跳过中间某些数如 [3,3,3,1,2,1,1,2,3,3,4]
//定义type2 出现在 k处
if((fruits[j] != type1) && (type2 == -1)){
k = i;
for(;k<=j;k++){
if(fruits[k] != fruits[i]){
type2 = fruits[k];
break;
}
}
}
cout << "type2出现的位置k :" << k <<endl;
//如果j位置的水果与type1 type2相同,sum++
if((fruits[j] == type1)||(fruits[j] == type2)){
sum++;
if(sum > result) {
result = sum; //way1
//result = j - i + 1;
cout<<"type1,2: " << type1 << " " <<type2<<endl;
cout<<"result (in i = " << i <<" j = "<<j<<" ) :"<<result<<endl;
}
}else{
// // way1
// cout << "i = " << i << endl;
// i++;
// j--; // 一定要!!!!!
// sum--;
// way2 直接跳i, i更新,sum也得更新
// i = j - 1;//绝对不对
sum = sum - (k - i); //sum得在i前更新
i = k;
j--;
type2 = -1;
cout << "i = " << i << endl;
cout<<"重置"<<endl;
}
}
return result;
}
};
int main(){
Solution904 sol;
int arr1[3] = {1,2,1};
vector<int> fruits1 ;
for(int i = 0;i<3;i++){
fruits1.push_back(arr1[i]);
cout<<fruits1[i]<<"";
}
cout << endl;
cout << "示例输出:" <<sol.totalFruit(fruits1) <<endl;
cout << endl;
int arr2[4] = {0,1,2,2};
vector<int> fruits2 ;
for(int i = 0;i<4;i++){
fruits2.push_back(arr2[i]);
cout << fruits2[i]<<" ";
}
cout << endl;
cout << "示例输出:" <<sol.totalFruit(fruits2) <<endl;
cout << endl;
int arr41[11] = {3,3,3,1,2,1,1,2,3,3,4};
vector<int> fruits41 ;
for(int i = 0;i<11;i++){
fruits41.push_back(arr41[i]);
cout << fruits41[i]<<" ";
}
cout << endl;
cout << "示例输出:" <<sol.totalFruit(fruits41) <<endl;
cout << endl;
int arr68[1] = {0};
vector<int> fruits68 ;
fruits68.push_back(arr68[0]);
cout << endl;
cout << "示例输出:" <<sol.totalFruit(fruits68) <<endl;
cout << endl;
int arr3[2] = {0,1};
vector<int> fruits3 ;
for(int i = 0;i<2;i++){
fruits3.push_back(arr3[i]);
cout << fruits3[i]<<" ";
}
cout << endl;
cout << "示例输出:" <<sol.totalFruit(fruits3) <<endl;
cout << endl;
int arr69[7] = {0,1,6,6,4,4,6};
vector<int> fruits69 ;
for(int i = 0;i<7;i++){
fruits69.push_back(arr69[i]);
cout << fruits69[i]<<" ";
}
cout << endl;
cout << "示例输出:" <<sol.totalFruit(fruits69) <<endl;
cout << endl;
int arr80[8] = {4,1,1,1,3,1,7,5};
vector<int> fruits80 ;
for(int i = 0;i<8;i++){
fruits80.push_back(arr80[i]);
cout << fruits80[i]<<" ";
}
cout << endl;
cout << "示例输出:" <<sol.totalFruit(fruits80) <<endl;
cout << endl;
return 0;
}
2、其他解法
来自leetcode评论区
(1)上面暴力解的改进版,在循环 j 时就顺便把第二种水果第一次出现的位置记录下来
力扣 C++ 双指针,可以用一个单独指针指向left下一次跳转的位置,也可以先left=right-1,再进行回退
class Solution { public: int totalFruit(vector<int>& fruits) { int left = 0, right = 0; // 双指针法,自己想到的! int firstSort = -1, secondSort = -1; int maxCount = 0; // 可以收集的最大数目 int thirdBefore = 0; // thirdBefore指向第三种果树的前一种果树的第一棵 firstSort = fruits[left]; for (right = 0; right < fruits.size(); right++) { if (fruits[right] != firstSort && secondSort == -1) { secondSort = fruits[right]; } if (fruits[right] == firstSort || fruits[right] == secondSort) { maxCount = right - left + 1 > maxCount ? right - left + 1 : maxCount; } else { left = thirdBefore; // 移动慢指针 firstSort = fruits[left]; secondSort = fruits[right]; } if (right > 0 && fruits[right] != fruits[right-1]) { thirdBefore = right; } } return maxCount; } 作者:meirikele 链接:https://leetcode.cn/problems/fruit-into-baskets/solution/by-meirikele-qpqu/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
(2)这道题是最大覆盖字串,和最小覆盖子串的区别