leetcode lecture on algorithm interview in Dachang 8. Sliding window
Video tutorial (efficient learning): Click to learn
catalog:
6. Depth first & breadth first
10. Recursion & divide and conquer
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,
- 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
- 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
- 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
- If there is only one fruit in the window, add the fruit to the arr array
- If there are two kinds of fruits, update the left boundary of the window and update the types of fruits in the arr
- If a new type of fruit comes in, update the location of the previous fruit
- 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; } }