leetcode-904 水果成篮
题目描述
在一排树中,第 i 棵树产生 tree[i] 型的水果。
你可以从你选择的任何树开始,然后重复执行以下步骤:
把这棵树上的水果放进你的篮子里。如果你做不到,就停下来。
移动到当前树右侧的下一棵树。如果右边没有树,就停下来。
请注意,在选择一颗树后,你没有任何选择:你必须执行步骤 1,然后执行步骤 2,然后返回步骤 1,然后执行步骤 2,依此类推,直至停止。
你有两个篮子,每个篮子可以携带任何数量的水果,但你希望每个篮子只携带一种类型的水果。
用这个程序你能收集的水果树的最大总量是多少?
示例 1:
输入:[1,2,1]
输出:3
解释:我们可以收集 [1,2,1]。
示例 2:
输入:[0,1,2,2]
输出:3
解释:我们可以收集 [1,2,2]
如果我们从第一棵树开始,我们将只能收集到 [0, 1]。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fruit-into-baskets
思路
我们可以创建两个指针left和right来标记我们收集到的果树的范围(如left=0,right=3那么我们能收集到的果树就是3-0+1=4棵),假如我们先选任意一颗树作为我们要收集的第一棵树(后面再说如何选择),因为这肯定是一颗全新的果树,所以我们需要一个果篮来专门收集这颗树上的水果,我们可以创建一个a使其等于left指针指向的值。然后另right以left为起点,开始向右移动,遇到第一个与a不同的值时停下(因为此时出现了一个新的果树),同理创建一个b使其等于right指针指向的值,然后令right继续向右移动,在right+1!=a&&right+1!=b处停下(当然也要注意是否会越界),因为此时又遇到了一颗新果树,而且已经没有果篮可以装新的水果了,然后再计算这一次收集了几颗果树。
至于如何选择left的值,毫无意外left一开始肯定在坐标零处,在每一次收集结束时,令left=right,如果左边有相同元素再令left一直左移。
因为按照规则 [left,right] 中间只能有两种水果,而且right+1肯定是第三种水果,所以我们要是继续执着于收集这两种水果,后续的范围一定必刚刚那中情况小,所以我们得舍弃一种水果,由于我们是从左向右遍历,所以只能保持最后遇见的那种水果,于是就有了上述选择方法。
代码
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int maxNum=0;
int left=0;
int right=0;
while(right<fruits.size()){
while(fruits[right]==fruits[left]&&right+1<fruits.size()){ //要注意越界问题
right++;
}
int a=fruits[left];
int b=fruits[right];
while(1){
if(right+1>=fruits.size()||fruits[right+1]!=a&&fruits[right+1]!=b){//同上
break;
}
right++;
}
maxNum=max(maxNum,right-left+1);
if(right==fruits.size()-1) break; //只剩一种水果了,溜了
left=right;
while(fruits[left-1]==fruits[left]){
left--;
}
}
return maxNum;
}
};
leetcode-76 最小覆盖子串
题目描述
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”
示例 2:
输入:s = “a”, t = “a”
输出:“a”
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-window-substring
思路
我们可以创建两个map,ht用于存储t中各个元素的数量,hs用于存储我们构建的“窗口”中各个元素数量,构建一个n=hs.size(),每当hs中键的值=ht中键的值时,n–,当n==0时,说明该窗口包括的字符串是一个符合要求的子串。
关于窗口:构建l和r都位于字符串起点,从字符串第一个字符开始判断是否属于字符串t,如果属于,则对应的hs的值++,并判断对应的hs是否等于对应的ht,如果相等,则n–,并判断n是否为零,等于零,说明找到一个符合条件的子串,然后做相应处理。
上述情况,都是关于指针r的移动,而关于指针l的移动,应在找到一个符合条件的子串之后,令l右移直至n>0。
简单来说,指针r一直右移直至找到刚好符合条件的点,然后再右移指针l直至找到刚好破坏条件的点,处理字符串则在每一次右移指针l之前。
代码
class Solution {
public:
string minWindow(string s, string t) {
unordered_map<char,int> ht,hs;
for(auto c:t) ht[c]++;
string res; //用于存储结果
int cnt=0; //用于计数已经有几个符合条件的位
int n=ht.size(); //总共有多少需求。。
int l=0,r=0;
while(r<s.size()&&ht.find(s[l])==ht.end()){ //提高一点效率,没用的就不管了
l++;
r++;
}
while(r<s.size()){
if(++hs[s[r]]==ht[s[r]]){//注意不能是>=,因为可能存在多个重复的元素
cnt++;
}
while(l<s.size()&&cnt==n){//找到破环条件的点之前一直循环
if(res.empty()||res.size()>(r-l+1)){
res=s.substr(l,r-l+1);
}
while(l<s.size()&&ht.find(s[l])==ht.end()){
l++;
}
if(--hs[s[l]]<ht[s[l]]){
cnt--;
}
l++;
}
r++;
}
return res;
}
};