滑动窗口
滑动窗口的本质其实就是双指针,使用left和right指针构成一个窗口
一般来说我们不固定窗口的大小,left = 0,right = 0
right指针:向右移动
left指针:当[left,right)里面满足了条件,这时候我们的left就需要向右移动,直到不再满足条件
再次移动right,直到再次满足条件,然后一直循环,直到无法构成窗口
滑动窗口解题模板
var minWindow = function (s:string, t:string) : string{
// need是target的集合
const need = new Map<string, number>();
// window是当前选中框的集合
const window = new Map<string, number>();
for (let value of t) {
if (need.has(value)) {
need.set(value, need.get(value) + 1)
} else {
need.set(value, 1)
}
}
// 设置两个边界,left和right
let left = 0;
let right = 0;
while (right < s.length) {
let d = s[right];
right++
//进行窗口内数据的更新
//当满足条件,进行收缩
while (valid === need.size) {
if (right - left < len) {
start = left
len = right - left
}
// 开始收缩
let e = s[left]
left++
//进行窗口的数据更新
//
}
}
return ....
};
个人觉得,滑动窗口一般都是给你两个字符串,然后在S中找T啥的
一定要注意好收缩的条件,就是啥时候收缩
滑动窗口例题
最小覆盖字串
解题思路:
- 在字符串
s中使用双指针,初始化left = 0,right = 0 - 首先我们不断增加
right,直到满足条件,我们就不在增加left left这时候继续向右,这样直到不在满足条件为止,这时候再继续,向右移动right- 就这样一直重复,直到
right到达字符串s的尽头
var minWindow = function (s, t) {
// need是target的集合
let need = new Map();
// window是当前选中框的集合
let window = new Map();
for (let value of t) {
if (need.has(value)) {
need.set(value, need.get(value) + 1)
} else {
need.set(value, 1)
}
}
// 设置两个边界,left和right
let left = 0;
let right = 0;
// valid意思是满足的情况
let valid = 0;
// 判断是不是满足条件,需要收缩
// 记录最小
let len = Number.MAX_VALUE
let start = 0
while (right < s.length) {
let d = s[right];
right++
if (need.has(d)) {
window.set(d, window.get(d) ? window.get(d) + 1 : 1)
if (window.get(d) === need.get(d)) {
valid++
}
}
while (valid === need.size) {
if (right - left < len) {
start = left
len = right - left
}
// 开始收缩
let e = s[left]
left++
if (need.has(e)) {
if (window.get(e) === need.get(e)) {
valid--;//valid不符合条件
}
window.set(e, window.get(e)-1)
}
}
}
return len === Number.MAX_VALUE ? '' : s.slice(start, start+len)
};
最长不包含重复字符的子字符串
链接:剑指 Offer 48. 最长不含重复字符的子字符串 - 力扣(LeetCode)
思路:
- 依然是使用滑动窗口,维护两个指针
left和right - 维护一个
map,用来判断是不是已经重复 right先向右移动,当map中的已经有值>1,也就是,已经有了重复了,进行收缩left收缩,因为返回的值是长度,所以我们时刻关注长度问题- 一直进行下去,直到
right到达尽头
代码:
function lengthOfLongestSubstring(s: string): number {
// 滑动窗口
let left = 0;
let right = 0;
let res = 0;
// 维护一个map
const window = new Map();
while(right<s.length){
let c = s[right];
right++
if(window.has(c)){
window.set(c,window.get(c)+1)
}else{
window.set(c,1)
}
// 如果这个值,他已经大于1,那么就说明,需要收缩
while(window.get(c)>1){
let d = s[left]
left++
window.set(d,window.get(d)-1)
}
res = Math.max(res,right-left)
}
return res
};
字符串的排列
题解:
- 同样的采用滑动窗口,解题思路和第一题类似
- 但是,这边变简单了,因为他只要求我们判断是不是
- 也就是我们只需要判断
valid === need.size就好了,为真,那么肯定存在 - 但是需要注意收缩的条件
- 题目说,要想
s1是s2的子串,那么也就意味着,他们的长度是肯定大于等于s1的,假如小于,肯定不存在了
代码:
function checkInclusion(s1: string, s2: string): boolean {
// 判断两个
const need = new Map<string,number>();
const window = new Map<string,number>()
let left = 0;
let right = 0;
let start = 0;
let valid = 0;
for(let value of s1){
if(need.has(value)){
need.set(value,need.get(value)+1)
}else{
need.set(value,1)
}
}
// 这时候我们进行区间
while(right<s2.length){
let c = s2[right]
right++
if(need.has(c)){
if(window.has(c)){
window.set(c,window.get(c)+1)
}else{
window.set(c,1)
}
// 判断是不是满足条件
if(need.get(c) === window.get(c)){
valid++
}
}
// 判断是不是都符合条件
while(right-left >= s1.length){
// 成立的条件是啥
if(need.size === valid){
return true
}
// 那么就可以收缩
let d = s2[left]
left++;
// 判断need里面是不是需要
if(need.has(d)){
// 那么就说明不在满足情况
// 并且刚好满足条件
if(need.get(d) === window.get(d)){
valid--
}
// 那么相对应的,那么window的值减一
window.set(d,window.get(d)-1);
}
}
}
return false
};
参考链接: