Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LeetCode] sliding window algorithm #159

Open
plh97 opened this issue Aug 15, 2020 · 0 comments
Open

[LeetCode] sliding window algorithm #159

plh97 opened this issue Aug 15, 2020 · 0 comments
Assignees
Labels
algorithm algorithm javaScript 关于js的一些事 学习 如果不学习,那今天和昨天又有什么区别 看书 其实如果不看书的话,那么每天写的东西都和昨天一样,又有什么意思

Comments

@plh97
Copy link
Owner

plh97 commented Aug 15, 2020

学习过程记录

第一次接触滑动窗口算法是在 TCP 协议中, 超时重发机制. 用的就是滑动窗口算法, 用于保证丢失的数据能够重传.

模板

var slidingWindow = function(s, t) {
    let l = 0
    let r = 0
    const sz = s.length
    while(r<sz) { // 是否需要扩张窗口
        const currR = s[r]
        r++
        console.log(s.slice(l,r))  // debug here
        while(isShrink()) { // 是否需要缩小窗口
            const currL = s[l]
            l++
        }
    }
};

要点

  1. left 和 right 两个指针

  2. 外层 while 代表是否需要扩张窗口, 当右边指针到边之前都是 right++

  3. 里层 while 代表是否需要缩小窗口, 当窗口内的数据不符合要求 left++

  4. 当窗口内的数据符合要求的时候, 记录下来窗口长度, 并输出记录过的最短长度.


来一题: 76. Minimum Window Substring

image

思路

按照上面的模板写出来并不难, 但这题的优化策略如下

  1. 用 start 和 end 参数分别记录 最短窗口位置的起点和窗口长度. 只有当需要更新最短窗口的时候才更新他们. 这样可以避免多次截取字符串来拿到窗口内的字符串.

  2. 如何高效的判断窗口内的字符串是否符合要求? 第一思路自然是for循环某个字符串, 查看他的每一个字符是否在另一个里面, 但这样是 O(n^2) 了, 我们可以将 目标字符串 t 转化成map, 然后滑动窗口每次有新的字符串进入, map[key]--, 如果窗口收缩的时候, map[key]++, 这样时时刻刻更新map, 最后我们只需要校验map中的每个值是否大于0即可高效的校验当前窗口内的字符串是否符合要求.

答案

var minWindow = function(s, t) {
    let l=0
    let r=0
    let start = 0
    let len = Infinity
    const t_map = {}
    for(let letter of t) {
        if (t_map[letter]) {
            t_map[letter]++
        }else{
            t_map[letter] = 1
        }
    }
    function isInclude(a) {
        for(let key in t_map) {
            if (t_map[key]>0) {
                return false
            }
        }
        return true
    }
    while(r<s.length) {
        // open
        if (t_map.hasOwnProperty(s[r])) {
            t_map[s[r]]--
        }
        r++
        while(isInclude()) {
            // shirly
            if (len>r-l) {
                start = l
                len = r-l
            }
            if (t_map.hasOwnProperty(s[l])) {
                t_map[s[l]]++
            }
            l++
        }
    }
    return len == Infinity ? '' : s.slice(start, start+len)
};

再来一题: 567. Permutation in String

image

思路

  1. 基本上和上面的是一模一样, 基本抛弃原先的多次遍历循环的解法了, 太低效了.

  2. 维护一个滑动的窗口, 这个窗口由 l, r 作为左节点和右节点进行维护.

  3. 针对 s1, 由于它是可以任意排序的, 因此对它生成一个map用于记录各个字母出现的次数.

  4. 滑动窗口什么时候扩张? 当r节点小于s2字符长度,

  5. 滑动窗口什么时候收缩? 当窗口长度小于 s1.length就收缩

  6. 滑动窗口扩张的时候, 拿到推入的那个字符串, 如果存在于 map, 则 map[key]--

  7. 滑动窗口收缩的时候, 拿到推出的那个字符串, 如果存在于 map, 则 map[key]++

  8. 收缩的同时, 校验map是否每个字符串都为 0, 如果为0, 则直接return true.

  9. 在最后return false, 即找不到符合条件的字符串.

答案

var checkInclusion = function(s1, s2,l=0,r=0) {
    const s1_map = s1.split('').reduce((m,l)=>{
        !m[l] && (m[l] = 0)
        m[l]++
        return m
    },{})
    while(r<s2.length) {
        // need to open
        if (s1_map.hasOwnProperty(s2[r])) {
            s1_map[s2[r]]--
        }
        r++
        while(r-l>=s1.length) {
            // console.log(s1_map)
            if (Object.values(s1_map).every(e=>e==0)) {
                return true
            }
            if (s1_map.hasOwnProperty(s2[l])) {
                s1_map[s2[l]]++
            }
            // need to shrink
            l++
        }
    }
    return false
};

再来一题: 438. Find All Anagrams in a String

image

思路

和上面的基本一致, 维护一个滑动窗口, 有符合条件直接把当前坐标推入 res 数组中.

答案

var findAnagrams = function (s, p) {
	const map = p.split('').reduce((map, e) => {
		if (map[e]) {
			map[e]++
		} else {
			map[e] = 1
		}
		return map
	}, {})
	const res = []
	let l = 0
	let r = 0
	while (r < s.length) {
		// open
		if (map.hasOwnProperty(s[r])) {
			map[s[r]]--
		}
		r++
		while (r - l >= p.length) {
			// reduce
            if (Object.values(map).every(val=>val==0)) {
                res.push(l)
            }
			if (map.hasOwnProperty(s[l])) {
				map[s[l]]++
			}
			l++
		}
	}
	return res
};

3. Longest Substring Without Repeating Characters

image

思路

同样是滑动窗口算法, 不过位置需要调转一下.

答案

/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s,max=0,l=0,r=0,map={}) {
    while(r<s.length) {
        if (map[s[r]]) {
            map[s[r]]++
        }else{
            map[s[r]] = 1
        }
        while(map[s[r]] > 1) {
            map[s[l]]--
            l++
        }
        r++
        max = Math.max(r-l,max)
    }
    return max
};

424. Longest Repeating Character Replacement

image

思路

这题我也不知道为什么对了, 反正框架写出来修修改改就是对的.

答案

/**
 * @param {string} s
 * @param {number} k
 * @return {number}
 */
var characterReplacement = function(s, k) {
    const map = {}
    for(let le of s){
        map[le] = 0
    }
    let l = 0;
    let r = 0;
    let res = 0
    function isRepeatStringMoreThanK(map) {
        const arr = Object.values(map)
        arr.sort((a,b)=>b-a)
        return arr.slice(1).reduce((t,e)=>t+e,0) > k
    }
    while(r<s.length) { // grow
        map[s[r]]++
        r++
        while(isRepeatStringMoreThanK(map)) { //shrink
            map[s[l]]--
            l++
        }
        res = Math.max(res, r-l)
    }
    return res
};

Reference

我写了套框架,把滑动窗口算法变成了默写题

@plh97 plh97 self-assigned this Aug 15, 2020
@plh97 plh97 added algorithm algorithm javaScript 关于js的一些事 学习 如果不学习,那今天和昨天又有什么区别 看书 其实如果不看书的话,那么每天写的东西都和昨天一样,又有什么意思 labels Aug 15, 2020
@plh97 plh97 changed the title 滑动窗口算法 滑动窗口 Aug 15, 2020
@plh97 plh97 changed the title 滑动窗口 sliding window algorithm Sep 8, 2020
@plh97 plh97 changed the title sliding window algorithm [LeetCode] sliding window algorithm Oct 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
algorithm algorithm javaScript 关于js的一些事 学习 如果不学习,那今天和昨天又有什么区别 看书 其实如果不看书的话,那么每天写的东西都和昨天一样,又有什么意思
Projects
None yet
Development

No branches or pull requests

1 participant