求最短/最小
一般题目都有「至少」的要求。
想窗口成立的条件,right右移增强条件,然后while循环left右移最终破坏条件
模版套路
在while循环内更新答案
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n=nums.size();
long long sum=0;
int res=INT_MAX;
int left=0;
for(int right=0;right<n;++right){
// 1.入窗口
sum+=nums[right];
// 2.满足条件
while(sum>=target){
// 2.1 更新答案
res=min(res,right-left+1);
// 2.2 出窗口
sum-=nums[left];
++left;
}
}
// 无答案
if(res==INT_MAX) return 0;
return res;
}
};
套路
题目描述
1.找出该数组中满足其总和大于等于 target
(窗口条件)的长度最小 的 子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度**。如果不存在符合条件的子数组,返回 0
。
2.如果 s
的某个子字符串中 1
的个数恰好等于 k
(窗口条件) ,则称这个子字符串是一个 美丽子字符串 。令 len
等于 最短 美丽子字符串的长度。返回长度等于 len
且字典序 最小 的美丽子字符串。如果 s
中不含美丽子字符串,则返回一个 空 字符串。(两重最小,长度最小+字典序最小)
3.假如在该字符串中,这四个字符都恰好出现 n/4
次 (题目条件,而不是窗口条件),那么它就是一个「平衡字符串」。给你一个这样的字符串 s
,请通过「替换一个子串 」的方式,使原字符串 s
变成一个「平衡字符串」。你可以用和「待替换子串」长度相同的 任何 其他字符串来完成替换。请返回待替换子串的最小可能长度 。(题意转换,窗口内字符是可以替换的,即窗口内字符是任意分配的(不用具体在意是什么),能够让窗口外字符数量增加,但不会减少,所以一个窗口满足的条件就是窗口外4种字符的数量都小于等于n/4)
学习经验
1.与求最长/最大不同,最长/最大右移right会导致窗口不满足条件,所以while循环内是不满足条件就右移left直到满足条件 ,while循环结束窗口满足条件,更新答案。(while外更新答案)
而求最短/最小,右移right肯定更加满足条件 ,所以while循环内 是满足条件就先更新答案,然后右移left,直到不满足条件 ,然后right接着右移。(while内更新答案)
另一种思想是while中先假设右移left,若满足条件就进入while,然后真正右移left,while循环结束窗口肯定也满足条件,再判断满足条件(刚开始不一定满足)然后更新答案(while外更新答案)
1. 209.长度最小的子数组(中等,学习)
思想
1.找出该数组中满足其总和大于等于 target
的长度最小的 子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度**。如果不存在符合条件的子数组,返回 0
2.我的想法比较复杂,想跟求最长/最大套路一样,窗口不满足条件更新窗口,但是这题右移right会一直满足条件,所以想到左移left,但是得先找到第一个满足条件的窗口。所以先右移right找到第一个满足条件的区间[0,right)
,然后依次左移left,不满足条件就while右移right,使得区间[left+1,right)
为窗口,但是因为right是开区间,会遇到无法再右移的情况,需要一个tag变量进行判断,并在res更新前判断tag退出for循环,调试了一会。
3.学习:
依旧是枚举右端点移动,简单直接
(1)在while循环结束后更新答案
- 1.right入窗口
- 2.假设 left左移,如果
sum-nums[left]>=target
,那么就while左移left - 3.满足条件(判断一开始不满足条件的情况,能左移说明左移完肯定满足条件)则更新res
(2)在while循环内更新答案(学习) - 1.right入窗口
- 2.满足要求进入while循环,先更新答案,后左移left
代码
c++ :
1.我的
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
long long sum = 0;
int right = 0;
while (right < n && sum < target) {
sum += nums[right];
++right;
}
if (sum < target)
return 0;
int res = right;
bool tag = false;
// 区间:[left+1,right)
for (int left = 0; left < n; ++left) {
sum -= nums[left];
while (sum < target) {
if (right == n) {
tag = true;
break;
}
sum += nums[right];
++right;
}
if (tag)
break;
res = min(res, right - left - 1);
}
return res;
}
};
2.在while循环外更新答案
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
long long sum = 0;
int left = 0;
int res = INT_MAX;
for (int right = 0; right < n; ++right) {
sum += nums[right];
while (sum - nums[left] >= target) {
sum -= nums[left];
++left;
}
if (sum >= target)
res = min(res, right - left + 1);
}
if (res == INT_MAX)
return 0;
return res;
}
};
3.在while循环内更新答案
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n=nums.size();
long long sum=0;
int res=INT_MAX;
int left=0;
for(int right=0;right<n;++right){
sum+=nums[right];
while(sum>=target){
res=min(res,right-left+1);
sum-=nums[left];
++left;
}
}
if(res==INT_MAX) return 0;
return res;
}
};
2. 2904.最短且字典序最小的美丽子字符串(中等)
2904. 最短且字典序最小的美丽子字符串 - 力扣(LeetCode)
思想
1.如果 s
的某个子字符串中 1
的个数恰好等于 k
(窗口条件) ,则称这个子字符串是一个 美丽子字符串 。令 len
等于 最短 美丽子字符串的长度。返回长度等于 len
且字典序 最小 的美丽子字符串。如果 s
中不含美丽子字符串,则返回一个 空 字符串。
2.这题要求答案字符串长度最短且字典序最小,两重最小
3.字符串初始设置最大值string strmax(n+1,'z')
4.字符串重要的substr(pos,len)函数
代码
c++:
class Solution {
public:
string shortestBeautifulSubstring(string s, int k) {
int n = s.size();
long long sum1 = 0;
string strmax(n + 1, 'z'); // 初值最大值,等价于INT_MAX
string res = strmax;
int left = 0;
for (int right = 0; right < n; ++right) {
if (s[right] == '1')
++sum1;
while (sum1 == k) {
string t = s.substr(left, right - left + 1);
if (t.size() < res.size()) {
res = t;
} else if (t.size() == res.size()) {
res = min(res, t);
}
if (s[left] == '1')
--sum1;
++left;
}
}
if (res == strmax)
return "";
return res;
}
};
3. 1234.替换子串得到平衡字符串(中等,学习思想)
1234. 替换子串得到平衡字符串 - 力扣(LeetCode)
思想
1.假如在该字符串中,这四个字符都恰好出现 n/4
次,那么它就是一个「平衡字符串」。给你一个这样的字符串 s
,请通过「替换一个子串 」的方式,使原字符串 s
变成一个「平衡字符串」。你可以用和「待替换子串」长度相同的 任何 其他字符串来完成替换。请返回待替换子串的最小可能长度 。
2.题意转换,窗口内字符是可以替换的,即窗口内字符是任意分配的(不用具体在意是什么),能够让窗口外字符数量增加,但不会减少,所以一个窗口满足的条件就是窗口外4种字符的数量都小于等于n/4, 从而窗口内的字符能分配使总字符都为n/4
所以需要维护每种字符窗口外的数量,利用map
代码
c++:
class Solution {
public:
int balancedString(string s) {
int n = s.size();
int avg = n / 4;
unordered_map<char, int> cnt;
for (char c : s)
++cnt[c];
if (cnt['Q'] == avg && cnt['W'] == avg && cnt['E'] == avg &&
cnt['R'] == avg)
return 0;
int res = INT_MAX;
int left = 0;
for (int right = 0; right < n; ++right) {
// 入窗口相当于窗口外字符数量减少
--cnt[s[right]];
while (cnt['Q'] <= avg && cnt['W'] <= avg && cnt['E'] <= avg &&
cnt['R'] <= avg) {
res = min(res, right - left + 1);
// 出窗口相当于窗口外字符数量增加
++cnt[s[left++]];
}
}
return res;
}
};