【Optimized Algorithm】——Sliding window problem

In this issue, I will bring you an introduction to sliding window algorithms, and help you think and understand through specific topics.

C language sliding window algorithm - CSDN 

 

Table of contents

(1) Basic concepts

(2) Topic explanation

1. Difficulty: medium

1️⃣The subarray with the smallest length

2️⃣ Find all anagrams in the string

2. Difficulty: hard

1️⃣ Minimum covering substring

2️⃣ Concatenate the substrings of all words

Summarize


(1) Basic concepts

Sliding Window Algorithm (Sliding Window Algorithm) is a common algorithmic technique used to solve some array or string related problems. The algorithm is based on the concept of window, moves within a fixed-size window, and performs appropriate adjustments and calculations according to specific problems.

The working principle and application scenarios of the sliding window algorithm will be introduced in detail below:

How it works :

  • Window Size : The sliding window algorithm solves the problem by setting a window size. A window is usually a contiguous subarray or substring.
  • Initialize the window : initialize the starting position of the window, and set the size of the window according to the problem requirements.
  • Moving window : By moving the starting position of the window, the size and position of the window are continuously adjusted to find a solution that satisfies the problem conditions.
  • Update Solution : Based on window movement and resizing, update the solution to the problem and record or return the desired result.

Application scenario:

  • Min/Max Subarray/Substring : Finds the smallest or largest subarray or substring in a given array or string that satisfies certain conditions.
  • String matching : Find occurrences of another string in a string or substrings that meet certain conditions.
  • Combination of sliding window and hash table : optimize the sliding window algorithm by using hash table to improve efficiency.
  • Optimize window size : According to the characteristics of the problem, adjust the window size to find the best solution.

The steps of the sliding window algorithm are usually as follows:

  1. Initialize the start and end positions of the window so that they meet the requirements of the problem.
  2. Enter a loop, move the start position and end position of the window continuously until the window slides to the end of the array or string.
  3. In each loop, check whether the elements in the window satisfy the requirements of the question. If the condition is met, update the solution or perform other operations. If the condition is not met, keep moving the window.
  4. When moving the window, the elements and corresponding data structures in the window should be updated to ensure the correctness of the window.
  5. Repeat steps 2 to 4 until the entire array or string is traversed and the solution or desired result is returned.


(2) Topic explanation

Next, we will give you a specific experience through a few topics. (topics from easy to difficult )

1. Difficulty: medium

1️⃣The subarray with the smallest length

Title link: 209. The smallest length subarray

 【Title Description】

[Solution] ( sliding window )

Algorithm idea:
Since the object of analysis of this problem is "a continuous interval", the idea of ​​"sliding window" can be considered to solve this problem.
Let the sliding window satisfy: starting from position i, the sum of all elements in the window is less than target (then when the sum of elements in the window is greater than or equal to the target value for the first time, it starts at position i, and the condition is
met minimum length).


Method : divide the elements at the right end into the window, and count the sum of the elements in the window at this time:

  • If the sum of the elements in the window is greater than or equal to the target: update the result, and continue to judge whether the condition is met and update the result while drawing out the left end element (because the left end element may be very small, the condition is still met after being drawn out)
  • If the sum of elements in the window does not satisfy the condition: right++ , another next element enters the window.
     

【Principle Explanation】

Many problem solutions and posts only tell you how to do it, but don't explain why you do it. i.e. why sliding window can solve the problem and has lower time complexity?


▪ What this window is looking for is: based on the leftmost element of the current window (denoted as left1), the situation that meets the conditions. That
is, in this question, starting from left1,
where the rightmost side (denoted as right1) can go when the interval and sum >= target are satisfied.


▪ Now that we have found the optimal interval starting from left1, we can boldly discard left1. However,
if we start counting the sum of the second element ( left2 ) again at this time, there will inevitably be a lot of repeated calculations (because we have already calculated the sum of many elements when calculating the first interval, these sums It can be used when calculating the next interval sum).


▪ At this point, the role of rigth1 is reflected, we only need to remove the value of left1 from the sum. Start with
the right1 element, and then find the interval that satisfies the left2 element (at this time, right1 may also be satisfied, because left1 may be very small. After the sum removes left1, it still satisfies greater than or equal to target). In this way, we can save a lot of repeated calculations.


▪ In this way we can not only solve the problem, but also greatly improve the efficiency
 

【Algorithm implementation】

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size(), sum = 0, len = INT_MAX;
        for(int left = 0, right = 0; right < n; right++)
        {
            sum += nums[right]; // 进窗⼝
            while(sum >= target) // 判断
            {
                len = min(len, right - left + 1); // 更新结果
                sum -= nums[left++]; // 出窗⼝
            }
        }
    return len == INT_MAX ? 0 : len;
    }
};

【Result display】

 【Performance analysis】

 The specific performance analysis is as follows:

  • Time complexity : Since each element is traversed at most twice (in and out of the window), the time complexity is linear. So the time complexity is O(n) , where n is the length of the array nums.
  • Space complexity : Only a few extra variables are used in the code, which does not change with the input scale, so the space complexity is O(1) .

 

2️⃣ Find all anagrams in the string

Title link: 438. Find all anagrams in a string

 

【Title Description】

[Solution] (sliding window + hash table)


Algorithm idea:

  1. Since the length of the anagram of the string p must be the same as the length of the string p, we can construct a sliding window in the string s whose length is the same as the length of the string p, and Maintain the number of each type of letter in the window during sliding;
  2. When the number of each letter in the window is the same as the number of each letter in the string p, it means that the current window is an anagram of the string p;
  3.  Therefore, two arrays of size 26 can be used to simulate a hash table, one to store the number of occurrences of each character in the substring in s, and the other to store the number of occurrences of each character in p. In this way, it can be judged whether the two strings are anagrams.

【Algorithm implementation】

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> ret;
        int hash1[26] = { 0 }; // 统计字符串 p 中每个字符出现的个数
        for(auto ch : p) 
            hash1[ch - 'a']++;
        int hash2[26] = { 0 }; // 统计窗⼝⾥⾯的每⼀个字符出现的个数
        int m = p.size();
        for(int left = 0, right = 0, count = 0; right < s.size(); right++)
        {
            char in = s[right];
            // 进窗⼝ + 维护 count
            if(++hash2[in - 'a'] <= hash1[in - 'a']) 
                count++;
            if(right - left + 1 > m) // 判断
            {
                char out = s[left++];
                // 出窗⼝ + 维护 count
                if(hash2[out - 'a']-- <= hash1[out - 'a']) 
                    count--;
            }
            // 更新结果
            if(count == m) 
                ret.push_back(left);
        }
        return ret;
    }
};

【Result display】

 【Performance Analysis】

The specific performance analysis is as follows:

  • Time complexity : The code uses double pointers to traverse the string s, so the time complexity is O(n) , where n is the length of the string s.
  • Space complexity : Two hash arrays of size 26 are used in the code, so the space complexity is O(1) .

2. Difficulty: hard

1️⃣ Minimum covering substring

The link is as follows: 76. Minimum Covering Substring

【Title Description】

 

[Solution]  ( sliding window + hash table )

Algorithm idea:
◦ The research object is a continuous interval, so you can try to use the idea of ​​sliding window to solve it.
◦ How to judge that all the characters in the current window meet the requirements?

  • We can use two hash tables, one of which collects the information of the target string, and the other hash table dynamically maintains the information of the string within the window.
  • When the dynamic hash table contains all the characters in the target string, and the corresponding number is not less than the number of each character in the hash table of the target string, then the current window is a feasible method case

【Algorithm flow】

a. Define two global hash tables: No. 1 hash table hash1 is used to record the substring information, and No. 2 hash table hash2 is used to record the information of the target string t;


b.  Implement an interface function to judge whether the current window meets the requirements:
    i. Traverse the elements at the corresponding positions in the two hash tables:
        • If the number of a certain character in t is greater than the number of characters in the window, That is, a certain position of the No. 2 hash table is greater than that of the No. 1 hash table. If no match is specified, return false;
        • If all match, return true

In the main function:
a. First put the information of t into the No. 2 hash table;
b. Initialize some variables: left and right pointers: left = 0, right = 0; length of the target substring: len =
INT_MAX; The starting position of the target substring: retleft; (through the starting position and length of the target substring, we can
find the result)
c. When right is less than the length of the string s, the following loop continues:

  • i. Throw the currently traversed elements into the No. 1 hash table;
  • ii. Check whether the current window meets the conditions:

• If the conditions are met:
        ◦ Determine whether the current window becomes smaller. If it becomes smaller: update the length len and the starting position
retleft of the string;
        ◦ After the judgment is completed, slide the left element out of the window and update the No. 1 hash table by the way;
        ◦ Repeat the above two processes until the window The conditions are not met;

  • iii. right++ , traverse the next element;

d. Determine whether the length of len is equal to INT_MAX:

  • i. If they are equal, it means there is no match, and an empty string is returned;
  • ii. If you don't want to wait, it indicates a match, and returns a string of length len from the retleft position in s.

【Algorithm implementation】

class Solution {
public:
    string minWindow(string s, string t) {
        int hash1[128] = { 0 }; // 统计字符串 t 中每⼀个字符的频次
        int kinds = 0; // 统计有效字符有多少种
        for(auto ch : t)
        if(hash1[ch]++ == 0) kinds++;
        int hash2[128] = { 0 }; // 统计窗⼝内每个字符的频次
        int minlen = INT_MAX, begin = -1;
        for(int left = 0, right = 0, count = 0; right < s.size(); right++)
        {
            char in = s[right];
            if(++hash2[in] == hash1[in]) count++; // 进窗⼝ + 维护 count
            while(count == kinds) // 判断条件
            {
                if(right - left + 1 < minlen) // 更新结果
                {
                    minlen = right - left + 1;
                    begin = left;
                }
                char out = s[left++];
                if(hash2[out]-- == hash1[out]) count--; // 出窗⼝ + 维护 count
            }
        }
        if(begin == -1) 
            return "";
        else 
            return s.substr(begin, minlen);
    }
};

【Result display】

 

【Performance Analysis】

The specific performance analysis is as follows:

  • Time complexity : The time complexity of the algorithm is O(n) , where n is the length of the string s. The code uses a hash table to record frequency information and achieves linear time complexity.
  • Space complexity : The storage space of the hash tables hash1 and hash2 is an array of 128 elements, so the space complexity is O(1) .


2️⃣ Concatenate the substrings of all words

The link is as follows : 30. Concatenate the substrings of all words

【Title Description】

 

 【solution】

Algorithm idea:


If we treat each word as a letter, the problem becomes to find "all the anagrams in the string". It is nothing
more than that the objects processed before are characters one by one, and the objects we process here are words one by one.
 

Therefore, this question is left to you to do it yourself! !


Summarize

The above is the main content of this issue about the sliding window algorithm. Thank you for watching and supporting! ! !

 

Guess you like

Origin blog.csdn.net/m0_56069910/article/details/131679031
Recommended