leetcode lecture on algorithm interview in Dachang 8. Sliding window

Posted by ierpe on Sat, 27 Nov 2021 02:28:20 +0100

leetcode lecture on algorithm interview in Dachang 8. Sliding window

Video tutorial (efficient learning): Click to learn

catalog:

1. Introduction

2. Time and space complexity

3. Dynamic planning

4. Greed

5. Binary search

6. Depth first & breadth first

7. Double pointer

8. Sliding window

9. Bit operation

10. Recursion & divide and conquer

11 Pruning & backtracking

12. Reactor

13. Monotone stack

14. Sorting algorithm

15. Linked list

16.set&map

17. Stack

18. Queue

19. Array

20. String

21. Trees

22. Dictionary tree

23. Consolidation

24. Other types of questions

3. Longest substring without repeated characters (medium)

Method 1. Sliding window

The animation is too large. Click to view it

  • Idea: the sliding window keeps moving forward. If the current element is not in the set, add the set, and then update the maximum length. i + + continues the next cycle. If there are duplicate elements in the set, keep j + + and delete the elements outside the window until there are no duplicate elements in the sliding window
  • Complexity: time complexity O(n), n is the length of the string. The space complexity is O(n), that is, the space of set, and the worst case is O(n)

js:

var lengthOfLongestSubstring = function (s) {
    const set = new Set(); //Determine whether there are duplicate elements in the sliding window
    let i = 0,//Sliding window right border
        j = 0,//Sliding window left border
        maxLength = 0;
    if (s.length === 0) {//Extreme situation
        return 0;
    }
    for (i; i < s.length; i++) {
        if (!set.has(s[i])) {//If the current element is not in set, add set, and then update the maximum length. i + + continues the next cycle
            set.add(s[i]);
            maxLength = Math.max(maxLength, set.size);
        } else {
            //There are duplicate elements in set. Keep j + + and delete the elements outside the window until there are no duplicate elements in the sliding window
            while (set.has(s[i])) {
                set Sliding window traverses the string and keeps changing.delete(s[j]);
                j++;
            }
            set.add(s[i]);//Don't worry about adding s[i] to set
        }
    }
    return maxLength;
};

Java:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        Set<Character> set = new HashSet<Character>();
        int n = s.length();
        int j = -1, ans = 0;
        for (int i = 0; i < n; ++i) {
            if (i != 0) {
                set.remove(s.charAt(i - 1));
            }
            while (j + 1 < n && !set.contains(s.charAt(j + 1))) {
                set.add(s.charAt(j + 1));
                ++j;
            }
            ans = Math.max(ans, j - i + 1);
        }
        return ans;
    }
}

219. Duplicate Element II exists (easy)

The animation is too large. Click to view it

Method 1: sliding window
  • Idea: loop the array, and constantly add elements to the sliding window, that is, add set. If there are duplicate elements in the set and the window size is less than the specified size, return. Otherwise, add it to the set. When the sliding window exceeds the specified size, reduce the window
  • Complexity: time complexity O(n), space complexity O(min(n, k))

js:

var containsNearbyDuplicate = function(nums, k) {
    const set = new Set();
    for(let i = 0; i < nums.length; i++) {
        if(set.has(nums[i])) {//Duplicate element found
            return true;
        }
        set.add(nums[i]);//If not found, join the set to expand the window
        if(set.size > k) {//The sliding window exceeds the specified size. Reduce the window
            set.delete(nums[i - k]);
        }
    }
    return false;
};

java:

class Solution {
    public boolean containsNearbyDuplicate(int[] nums, int k) {
        HashSet<Integer> set = new HashSet<>();
        for(int i = 0; i < nums.length; i++) {
            if(set.contains(nums[i])) {
                return true;
            }
            set.add(nums[i]);
            if(set.size() > k) {
                set.remove(nums[i - k]);
            }
        }
        return false;
    }
}

76. Minimum coverage substring (hard)

Method 1. Sliding window
  • Idea: use the left and right pointers to traverse the s string. When the characters in the sliding window cannot cover the characters in T, move the right pointer to the right, expand the window, and add the characters on the right to the sliding window. When the characters in the sliding window can cover the characters in t, keep moving the left pointer to the left and narrow the window until the characters in the window just cover the characters in t, At this time, the characters in t cannot be overwritten when moving to the left. During the movement of the pointer, the minimum covering substring is constantly updated
  • Complexity: time complexity o(n), n is the length of s, space complexity o(t), t is the size of character set

The animation is too large. Click to view it

js:

var minWindow = function (s, t) {
    let need = {};//Frequency of strings to be overwritten
    let window = {};//String frequency of sliding window
    for (let a of t) {
        need[a] = (need[a] || 0) + 1;//Count the frequency of characters in t
    }
    //Left and right pointer
    let left = 0,
        right = 0;
    let valid = 0;//The number of character types that can be covered in the sliding window
    let start = 0,//Starting cable of minimum covering substring
        len = Number.MAX_VALUE;//Minimum covering substring length
    while (right < s.length) {
        let c = s[right];//Enter the character on the right of the sliding window
        right++;//Move window right
        if (need[c]) {//If the current character is in the need character, update the number of characters in the window
            window[c] = (window[c] || 0) + 1;
            if (window[c] == need[c]) {//If the current window and the required number of characters are the same, the character type + 1
                valid++;
            }
        }

        while (valid == Object.keys(need).length) {//When the character type is consistent with the required number of characters, the window will be narrowed
            if (right - left < len) {//The length of the current window is less than the length of the previous window. len updates the starting position and length of the minimum coverage substring
                start = left;
                len = right - left;
            }
            let d = s[left];//Characters to be removed
            left++;//Move window left removes characters from the window
            if (need[d]) {//If the number of characters in the window is updated in the required characters
                if (window[d] == need[d]) {//If the current window is consistent with the required number of characters, the character type is - 1
                    valid--;
                }
                window[d]--;
            }
        }
    }
    //The override substring '' was not found, otherwise the override substring will be returned
    return len == Number.MAX_VALUE ? "" : s.substr(start, len);
};


Java:

class Solution {
    public String minWindow(String s, String t) {
        Map<Character,Integer> needMap = new HashMap<Character,Integer>();
        Map<Character,Integer> windowsMap = new HashMap<Character,Integer>();
        for(char c : t.toCharArray()){
            needMap.put(c,needMap.getOrDefault(c,0)+1);
        }
        int left = 0,right = 0;
        int valid = 0;
        int start = 0,end = 0,len = Integer.MAX_VALUE;
        while(right < s.length()){
            char c = s.charAt(right);
            right++;
            if(needMap.containsKey(c)){
                windowsMap.put(c,windowsMap.getOrDefault(c,0)+1);
                if(windowsMap.get(c).equals(needMap.get(c))){
                    valid++;
                }
            }
            while(valid == needMap.size()){
                if(right - left< len){
                    len = right - left;
                    start = left;
                }
                char d = s.charAt(left);
                left++;
                if(windowsMap.containsKey(d)){
                    if(windowsMap.get(d).equals(needMap.get(d))){
                        valid--;
                    }
                    windowsMap.put(d,windowsMap.getOrDefault(d,0)-1);
                } 
            }
        }
        return len == Integer.MAX_VALUE ? "" : s.substring(start,start+len);
    }
}

438. Find all letter words in the string (medium)

The animation is too large. Click to view it

  • Idea: use the idea of sliding window to traverse the string,
    1. Judge whether the character entering the window is the required character, and whether the number of characters after adding the window is consistent with the number of characters in need
    2. Judge whether the characters in the window are the required characters, and whether the number of characters in the window is consistent with the number of characters in need
    3. Judge whether the characters that meet the requirements in the window and need are consistent. If they are consistent, the substring formed by this window is an ectopic word
  • Complexity: time complexity O(n), n is the length of the string. Space complexity O(k), K is the space of the character set

js:

//Writing method 1
var findAnagrams = function (s, p) {
    let need = {};//Required characters
    let win = {};//Characters in the window
    for (let a of p) {//Count the number of ectopic words
        need[a] = (need[a] || 0) + 1;
    }
    //Left and right pointer
    let left = 0,
        right = 0;
    let val = 0;//The character type with the same number of characters in the window and need
    let res = [];
    while (right < s.length) {
        let c = s[right];
        right++;//The character on the right enters the window
        if (need[c]) {
            win[c] = (win[c] || 0) + 1;//If the current character is in need, update the number of characters in the window
            if (win[c] == need[c]) {
                val++;//When the character matches the character in need in the window, the character type + 1
            }
        }
        while (right - left >= p.length) {//Keep coming out of the window
            if (val == Object.keys(need).length) {//If the substring and p in the window are ectopic words, add the left boundary to res
                res.push(left);
            }
            let d = s[left];
            left++;//Out window
            if (need[d]) {//If the character is in need, update the number and type of characters in the window
                if (win[d] == need[d]) {
                    val--;
                }
                win[d]--;
            }
        }
    }
    return res;
};

//Writing method 2
var findAnagrams = function (s, p) {
    //res: returned result
    //win: store the characters in the window and the corresponding frequency
    //need: the type and number of ectopic words needed for storage
    //len: character type of need ectopic word
    //val: the character type with the same number of characters in the sliding window and need
    const res = [], win = {}, need = {}, pLen = p.length;
    let len = 0, val = 0;
    for (const x of p) {//Cycle p 
        //If the character does not exist in need, initialize the number of corresponding characters in the need array and add 1 to the character type
        if (need[x] === undefined) {
            need[x] = win[x] = 0;
            len++;
        }
        need[x]++;//If the character exists in need, the number of characters is increased by 1
    }
    for (let i = 0; i < s.length; i++) {
        const j = i - pLen;//Sliding window left border
        //If the character s[i] entering the sliding window is in need, and the number of characters in the window plus 1 is the same as that in need,
      	//It indicates that the character has met the requirements of ectopic characters. Let val add 1
        if (s[i] in need && ++win[s[i]] === need[s[i]]) val++;
        //If the character s[j] of the sliding window is in need, and the number of characters in the sliding window is the same as that in need,
      	//Note: after removing the character from the window, the requirements for ectopic characters are not met. Reduce the number of characters in the window by 1 and val by 1
        if (s[j] in need && win[s[j]]-- === need[s[j]]) val--;
      	//If the types of ectopic characters in the sliding window in need are the same, it means that starting from j+1 is a starting point of ectopic string
        if (val === len) res.push(j + 1);
    }
    return res;
};


java:

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int[] need = new int[26];
        for (int i = 0; i < p.length(); i++) {
            need[p.charAt(i) - 'a']++;
        }
        int start = 0, end = 0;
        int[] window = new int[26];
        List<Integer> ans = new ArrayList<Integer>();
        while (end < s.length()) {
            window[s.charAt(end) - 'a']++;
            if (end - start + 1 == p.length()) {
                if (Arrays.equals(window, need)) ans.add(start);
                window[s.charAt(start) - 'a']--;
                start++;
            }
            end++;
        }
        return ans;
    }
}

1456. Maximum number of vowels in a fixed length substring (medium)

  • Idea: slide the window to traverse the string and constantly update the maximum number of vowels
  • Complexity: time complexity O(n), where n is the length of the string. Space complexity O(1)

js:

//Example: s=leetcode k=3
var maxVowels = function (s, k) {
    const vowels = new Set(['a', 'e', 'i', 'o', 'u'])
    let count = 0,
        l = 0,
        r = 0
    while (r < k) {//Initialize window size k
        vowels.has(s[r]) && count++
        r++
    }
    let max = count
    while (r < s.length) {//Keep moving windows
        vowels.has(s[r]) && count++
        vowels.has(s[l]) && count--
        l++
        r++
        max = Math.max(max, count)//Update maximum vowels
    }
    return max
};

java:

class Solution {
    public int maxVowels(String s, int k) {
        int n = s.length();
        int count = 0;
        for (int i = 0; i < k; ++i) {
            count += isVowel(s.charAt(i));
        }
        int ans = count;
        for (int i = k; i < n; ++i) {
            count += isVowel(s.charAt(i)) - isVowel(s.charAt(i - k));
            ans = Math.max(ans, count);
        }
        return ans;
    }

    public int isVowel(char ch) {
        return ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u' ? 1 : 0;
    }
}


904. Fruit baskets (medium)

The animation is too large. Click to view it

  • Idea: use the sliding window to traverse fruits. When a new kind of fruit enters the window
    1. If there is only one fruit in the window, add the fruit to the arr array
    2. If there are two kinds of fruits, update the left boundary of the window and update the types of fruits in the arr
    3. If a new type of fruit comes in, update the location of the previous fruit
    4. Update the maximum value of the sliding window
  • Complexity: time complexity O(n), space complexity O(1).

js:

//[1,1,2,2]
//[1,1,2,2,3] -> [2,2,3]
var totalFruit = function(fruits) {
    let l = 0;//Start pointer
    let maxLen = 0;//The maximum length of the window, which contains up to two kinds of fruits
    let n = 0//End position of the former fruit
    let arr = [fruits[l]]//Type array of fruit

    for(let r = 0; r < fruits.length; r++){//The right pointer of the window keeps moving forward
        if(!arr.includes(fruits[r])){//If the window does not contain the fruit in the window
            if(arr.length <= 1){//If there is only one fruit
                arr[1] = fruits[r]//Add this fruit to the arr array
            }else{//If there are two kinds of fruit
                l = n//Update the left border of the window
                arr[0] = fruits[r-1]//Update the types of fruits in arr
                arr[1] = fruits[r]
            }
        }
       
        if(fruits[r] !== fruits[n]){//If a new type of fruit comes in, update the location of the previous fruit
            n = r
        }

        maxLen = Math.max(maxLen,r-l+1)//Update the maximum value of the sliding window
    }
    return maxLen

};

java:

class Solution {
    public int totalFruit(int[] tree) {
        if (tree == null || tree.length == 0) return 0;
        int n = tree.length;

        Map<Integer, Integer> map = new HashMap<>();
        int maxLen = 0, left = 0;
        for (int i = 0; i < n; i++) {
            map.put(tree[i], map.getOrDefault(tree[i], 0) + 1); 
            while (map.size() > 2) { 
                map.put(tree[left], map.get(tree[left]) - 1);
                if (map.get(tree[left]) == 0) map.remove(tree[left]); 
                left++;
            }
            maxLen = Math.max(maxLen, i - left + 1);
        }
        return maxLen;
    }
}

Topics: leetcode