提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
介绍
具体来说,我们可以使用两个指针
l
e
f
t
left
left 和
r
i
g
h
t
right
right 分别表示滑动窗口的左右边界,然后通过不断移动右指针
r
i
g
h
t
right
right 来扩大窗口,同时根据问题的要求调整左指针
l
e
f
t
left
left 来缩小窗口。当右指针
r
i
g
h
t
right
right 扫描到字符串或数组的末尾时,算法的执行就完成了。
由于区间连续,因此当区间发生变化时,可以通过旧有的计算结果对搜索空间进行剪枝,这样便减少了重复计算,降低了时间复杂度
题目
1.无重复字符的最长子串
https://leetcode.cn/problems/wtcaE1/description/
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int l=0,r=0,n=s.length();
int ans=0;
while(r<n){
for(int i=r-1;i>=l;i--){
if(s[i]==s[r]){
l=i+1;
break;
}
}
ans=max(ans,r-l+1);
r++;
}
return ans;
}
};
2.最小覆盖子串
https://leetcode.cn/problems/M1oyTv/
class Solution {
public:
string minWindow(string s, string t) {
int len_s = s.length(), len_t = t.length();
long long int status = 0;
int t1[60], now[60];
memset(t1, 0, sizeof t1);
memset(now, 0, sizeof now);
for (int i = 0; i < len_t; i++) {
int idx = (t[i] - 'A');
if (!t1[idx])status ^=((long long)1 << idx);
t1[idx]++;
}
string ans;
int fl, fr;
int cnt = 0;
int l = 0, r = 0;
for (int i = 0; i < len_s; i++) {
int idx = (s[i] - 'A') ;
now[idx]++;
if ((status >> idx) & 1 && t1[idx] <= now[idx])status ^= ((long long)1 << idx);
if (!status) {
cnt = i - l + 1;
r = i;
fl = l, fr = r;
break;
}
}
if (status)return "";
while (r < len_s) {
int idx = (s[l] - 'A') ;
while (t1[idx] == 0 || now[idx] > t1[idx]) {
if (t1[idx] != 0)now[idx]--;
l++;
idx = (s[l] - 'A') ;
}
if (r - l + 1 < cnt) {
cnt = r - l + 1;
fl = l, fr = r;
}
r++;
int idxr = (s[r] - 'A') ;
if (r!=len_s && t1[idxr])now[idxr]++;
}
ans.assign(s, fl, cnt);
return ans;
}
};
3.加油站
https://leetcode.cn/problems/gas-station/description/
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int n=gas.size();
vector<int> chge(n+1);
for(int i=0;i<n;i++){
chge[i]=gas[i]-cost[i];
}
int sum=0;
for(int l=0,r=0;;r=(r+1)%n){
sum+=chge[r];
if(sum>=0){
if((r-l+n)%n==n-1)return l;
continue;
}
while(sum<0){
sum-=chge[l];
l=l+1;
if(l==n)return -1;
}
}
return -1;
}
};
4.替换子串得到平衡字符串
https://leetcode.cn/problems/replace-the-substring-for-balanced-string/description/
class Solution {
public:
int balancedString(string s) {
int cnt[90];
int n=s.length();
int want=n/4;
int len=1e5;
for(char c:s){
cnt[c]++;
}
if(cnt['Q']==want && cnt['W']==want && cnt['E']==want && cnt['R']==want)return 0;
for(int l=0,r=0;r<n;r++){
cnt[s[r]]--;
if(abs(cnt['Q']-want)+abs(cnt['W']-want)+abs(cnt['E']-want)+abs(cnt['R']-want)!=r-l+1){
continue;
}
while(abs(cnt['Q']-want)+abs(cnt['W']-want)+abs(cnt['E']-want)+abs(cnt['R']-want)==r-l+1){
cnt[s[l]]++;
l++;
}
if(r-l+2<len){
len=r-l+2;
}
}
return len;
}
};
5.k个不同整数的子数组
https://leetcode.cn/problems/subarrays-with-k-different-integers/
class Solution {
public:
int subarraysWithKDistinct(vector<int>& nums, int k) {
int sum1=0,sum2=0;
int n=nums.size();
int cnt[20003];
memset(cnt,0,sizeof cnt);
int now=0;
for(int l=0,r=0;r<n;r++){
if(cnt[nums[r]]==0)now++;
cnt[nums[r]]++;
while(now>k){
cnt[nums[l]]--;
if(cnt[nums[l]]==0)now--;
l++;
}
if(now<=k)sum1+=r-l+1;
}
if(k==1)return sum1;
memset(cnt,0,sizeof cnt);
now=0;
for(int l=0,r=0;r<n;r++){
if(cnt[nums[r]]==0)now++;
cnt[nums[r]]++;
while(now>k-1){
cnt[nums[l]]--;
if(cnt[nums[l]]==0)now--;
l++;
}
if(now<=k-1)sum2+=r-l+1;
}
return sum1-sum2;
}
};
6.至少有k个重复字符的最长子串
https://leetcode.cn/problems/longest-substring-with-at-least-k-repeating-characters/description/
class Solution {
public:
int longestSubstring(string s, int k) {
int n=s.length();
int ans=0;
int cnt[256];
for(int want=1;want<=26;want++){
memset(cnt,0,sizeof cnt);
for(int l=0,r=0,collect=0,satify=0;r<n;r++){
if(cnt[s[r]]==0)collect++;
cnt[s[r]]++;
if(cnt[s[r]]==k)satify++;
while(collect>want){
if(cnt[s[l]]==k)satify--;
cnt[s[l]]--;
if(cnt[s[l]]==0)collect--;
l++;
}
if(satify==want){
ans=max(r-l+1,ans);
}
}
}
return ans;
}
};
总结
单调性与范围相关
滑动窗口的逻辑带入到题目中,看是否合理
- 随着r增加,从不满足到满足,再调整l,又变为不满足,这种往往对应最小长度
- 随着r增加,从满足到不满足,再调整l,又变为满足,这种往往对应最大长度(或满足数目)
最后一题也就是第6题有些许特殊:通过技巧将题目翻译成只能含有i(1~26)种字符,并且每种字符数量大于等于k个的最长长度,最后取max;
翻译过后有两个限制条件,然后因为求的是最长子串,那么对应第二种,即在限制条件下,从满足到不满足。显然“大于等于k个”的限制条件不符合,而‘’只能含有i种字符”的条件好像符合,又好像不符合,为什么这样说呢,因为我们发现当i!=1的时候,随着r从0递增,子串是从不满足到满足然后最后到不满足的,既然如此,那能不能用滑动窗口呢,答案是肯定的,因为在判断是否符合语句中不满足不会进入判断,就没事啦~~