我们先来看代码模板
int func(vector<int> nums)
{
int left=0,right=0,var=0,ans=0,len=nums.size();//初始化left指针、right指针,再维护一个var变量,用来在指针移动过程中根据题意做出变化,再维护一个ans,也就是求的最小长度或者其他返回值
while (right<len)
{
//现在用nums[right]来更新var
while (var满足条件)
{
//更新最优结果ans
//用nums[left]来更新var
//窗口缩小,即left指针右移
}
right++;
}
return ans;
}
核心思想是:当你移动right指针发现该子序列满足条件时,就会想要右移指针来贪心看看有没没有更短的符合条件的子序列
注意,维护的var变量不一定是一个变量,你也可以用集合,只要var能用来衡量是否满足子序列条件即可
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其和 ≥ target
的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度**。**如果不存在符合条件的子数组,返回 0
。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
代码如下:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int left=0,right=0,len=nums.size(),sum=0,ans=len+1;
while (right<len)
{
sum+=nums[right];//维护sum
while (sum>=target)//右移left指针缩小滑动窗口大小
{
if (right-left+1<ans) ans=right-left+1;//更新最优结果
sum-=nums[left];//维护sum
left++;//左指针右移
}
right++;
}
if (ans==len+1) return 0;//有时候会有这样需要特殊判断的,假如本题完全没有符合条件的子序列,那么ans一定还是初始值,所以直接返回0
return ans;
}
};
解释:
int func(vector& nums)
{
int left=0,right=0,var=0,len=nums.size(),ans=0;
while (right
注意这里更新最优结果在while循环的外面,原因是因为while循环内其实都是不符合条件的子序列,所以无需更新最优结果
这里要注意的是while循环的条件变成了var不满足条件,因为此时的算法思想是:随着right指针右移,窗口越变越大,但如果窗口内的子序列不满足条件,就要右移left指针,直到条件重新符合
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
代码如下:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int left=0,right=0,len=s.size(),ans=0;
unordered_set<int> zifu;//注意这个其实就是var变量,用这个set来判断是否满足条件
while (right<len)
{
while (zifu.find(s[right])!=zifu.end())//发现此时有重复字符了
{
zifu.erase(s[left]);
left++;
}
if (right-left+1>ans) ans=right-left+1;
zifu.insert(s[right]);//注意用right来改变变量时,可以在while循环后,这样可以避免一插入就认为不符合条件的情况
right++;
}
return ans;
}
};
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits
表示,其中 fruits[i]
是第 i
棵树上的水果 种类 。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
给你一个整数数组 fruits
,返回你可以收集的水果的 最大 数目。
示例 1:
输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。
示例 2:
输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。
示例 3:
输入:fruits = [1,2,3,2,2]
输出:4
解释:可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。
示例 4:
输入:fruits = [3,3,3,1,2,1,1,2,3,3,4]
输出:5
解释:可以采摘 [1,2,1,1,2] 这五棵树。
代码:
class Solution {
public:
int totalFruit(vector<int>& fruits) {
unordered_map<int,int> count;
int left=0,right=0,ans=0,len=fruits.size();
while (right<len)
{
count[fruits[right]]++;
while (count.size()>2)
{
if (count[fruits[left]]==1) count.erase(fruits[left]);
else count[fruits[left]]--;
left++;
}
if (right-left+1>ans) ans=right-left+1;
right++;
}
return ans;
}
};
给定一个二进制数组 nums
和一个整数 k
,如果可以翻转最多 k
个 0
,则返回 数组中连续 1
的最大个数 。
示例 1:
输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。
示例 2:
输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。
代码:
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int left=0,right=0,len=nums.size(),ans=0,num=k;
while (right<len)
{
while (nums[right]==0 && k==0)
{
if (nums[left]==0) k++;
left++;
}
if (nums[right]==0 && k) k--;
if (right-left+1>ans) ans=right-left+1;
right++;
}
return ans;
}
};
给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
连续递增的子序列 可以由两个下标 l
和 r
(l < r
)确定,如果对于每个 l <= i < r
,都有 nums[i] < nums[i + 1]
,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]]
就是连续递增子序列。
示例 1:
输入:nums = [1,3,5,4,7]
输出:3
解释:最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。
示例 2:
输入:nums = [2,2,2,2,2]
输出:1
解释:最长连续递增序列是 [2], 长度为1。
代码如下:
class Solution {
public:
int findLengthOfLCIS(vector<int>& nums) {
int left=0,right=1,len=nums.size(),ans=-1;
if (len<2) return 1;
while (right<len)
{
while (nums[right]-nums[right-1]<=0 && left!=right)
{
left++;
}
if (right-left+1>ans) ans=right-left+1;
right++;
}
return ans;
}
};
int func(vector<int>& nums)
{
int left=0,right=0,var=0,len=nums.size(),ans=0;
while (right<len)
{
//用nums[right]更改var变量
if ((right-left+1)==target_length) //窗口大小达到指定值
{
if (满足条件) xxxxxxx;
//用nums[left]来更新var变量
left++;
}
right++;
}
}
给你两个字符串 s1
和 s2
,写一个函数来判断 s2
是否包含 s1
的排列。如果是,返回 true
;否则,返回 false
。
换句话说,s1
的排列之一是 s2
的 子串 。
示例 1:
输入:s1 = "ab" s2 = "eidbaooo"
输出:true
解释:s2 包含 s1 的排列之一 ("ba").
示例 2:
输入:s1= "ab" s2 = "eidboaoo"
输出:false
提示:
1 <= s1.length, s2.length <= 104
s1
和 s2
仅包含小写字母class Solution {
public:
bool checkInclusion(string s1, string s2) //s1是子串,s2是主串
{
int left=0,right=0,len1=s1.size(),len2=s2.size();
vector<int> map1(26,0),map2(26,0);//用来记录串的字母组成
for (auto i:s1)//先将子串统计好,用于跟主串窗口做对比
{
map1[i-'a']++;
}
//开始滑动窗口的主体
while (right<len2)
{
map2[s2[right]-'a']++;//窗口右移后,添加窗口中最后一位的字母
if ((right-left+1)==s1.size())//如果窗口长度达到目标值,判断窗口
{
if (judge(map1,map2))//判断是否是子串的某一种排列
{
return true;
}
map2[s2[left]-'a']--;//窗口右移,去除左边的字母
left++;
}
right++;//窗口右移
}
return false;
}
bool judge(const vector<int>& map1,const vector<int>& map2)//判断字母组成是否相同
{
for (int i=0;i<26;i++)
{
if (map1[i]!=map2[i]) return false;
}
return true;
}
};
【编程题】幼儿园有 N 个孩子玩游戏,随机围成了一个圈,老师最终想让所有男生排列到一起,所有女生排列到一起。每次老师可以命令两个孩子交换位置,求最小的命令次数:
N<=100
输入样例 1
3
FMF
输出样例 1
0
输入样例 2
4
FMFM
输出样例 2
1
int solution(int n, string s)
{
// 请添加具体实现
int cnt_f = 0, cnt_m = 0;
for (int i = 0; i < s.size(); i++)
{
switch (s[i])
{
case 'F':
cnt_f++;
break;
case 'M':
cnt_m++;
break;
};
}
// cout << "cnt_f=" << cnt_f
// << endl;
int left = 0, right = 0, cnt_F = 0, cnt_M = 0;
int target_len = cnt_f, min = INT_MAX;
// cout << "target_len=" << target_len
// << endl;
while (left < n)
{
// cout << "left=" << left << " right=" << right << endl;
if (s[right] == 'F')
cnt_F++;
// cout << "cnt_F=" << cnt_F << endl;
if (((right + n - left + 1) % n) == target_len)
{
if (target_len - cnt_F < min)
min = target_len - cnt_F;
if (s[left] == 'F')
cnt_F--;
left++;
}
right++;
if (right == n)
right = 0;
}
return min;
}