Python version-LeetCode learning: 76. Minimum coverage substring

Give you a string S and a string T. Please find in the string S: the smallest substring containing all the characters of T.

Example: Input: S = "ADOBECODEBANC", T = "ABC"
Output: "BANC"
Note: If there is no such substring in S, an empty string "" will be returned.
If there is such a substring in S, we guarantee that it is the only answer.

Source: LeetCode
Link: https://leetcode-cn.com/problems/minimum-window-substring

Method 1: This method mainly uses the sliding window idea, and its general meaning:

Use left, right to represent the left and right boundaries of the sliding window. By changing i, j to expand and contract the sliding window, you can imagine a window walking up the string. When the elements contained in this window meet the conditions, it contains characters All elements of the string T, record the length of the sliding window right-left +1, the minimum of these lengths is the required result

The specific implementation process is as follows:

(1) Calculate the number of each char in the target value t as the need list of char requirements, that is, k*v chars in need are required;

         Use: When a char in the sliding window is retrieved , the corresponding char value in need is offset, that is, need[char] -1; when a char is not found in the window function , the corresponding char in need is increased Value, ie need[char] +1;

(2) Record the length of the target value t, needCnt, that is, the total number of strings needed, and needCnt is equal to the sum of the value in need.

         Use: instead of traversing need, judge whether the sliding window contains all the chars in t by the total number

(3) Keep increasing the pointer to increase rightthe sliding window until the window contains all the elements of T: when the window contains all the need characters (ie needCnt=0), it means that the unnecessary characters enclosed in the window can be eliminated. Can shrink the window

(4) Keep increasing left to shrink the sliding window . Because the minimum string is required , unnecessary elements are excluded and the length is reduced until an element that must be included is encountered (in fact, the substring that must be included in the list need cannot be less than 0. When =0, it means The demand is just met) , this time can not be thrown away. Throwing again does not meet the conditions. Record the left and right values ​​of the sliding window at this time, right-left +1, which is the length of the minimum substring. And save the minimum value.

(5) When right adds another position and the sliding window does not meet the condition, this means moving left, which means that the sliding window increases until the window contains all the need characters (ie needCnt=0). At this time, from step (3), find a new sliding window that satisfies the conditions, and repeat until left exceeds the range of string S.

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        # 目标字符记录
        need=collections.defaultdict(int)
        for c in t:
            need[c]+=1
        # 目标字符长度
        needCnt=len(t)
        left=0
        res=(0,float('inf'))
        for r,c in enumerate(s):
            if need[c]>0:
                # 如果need[c]>0,意味着窗口搜索到目标值
                needCnt-=1
            # 目标值被搜索到,则need相应value-1,即用以记录搜索到need中字符的个数;
            # =0代表搜索到的值和need值相同;<0代表need中同一个字符被多次搜索到;
            # >0,代表need中该字符没有全部被遍历出来
            need[c]-=1   
            
            if needCnt==0: # 滑动窗口包含了所有T元素  
                # 开始窗口函数内部的搜索
                while True:  # 移动滑动窗口右边界i,排除多余元素
                    c=s[left] 
                    if need[c]==0:  
                        # need[c]==0,break 情况,代表着这轮的滑动窗口不符合要求
                        break
                    # 窗口中字符和need相互抵消
                    need[c]+=1
                    r+=1
                if r-left <res[1]-res[0]:   #记录结果
                    res=(left,r)
                # 窗口函数内部计算完后,向下一步迭代
                # s[left]是目标集中t的字符,这时从窗口函数清除,
                # 则意味着窗口函数需要新的字符填充,即need[s[left]]和needCnt需要加一
                need[s[left]]+=1  # left增加一个位置,寻找新的满足条件滑动窗口
                needCnt+=1
                # 新一轮的右边界
                r+=1
        return '' if res[1]>len(s) else s[res[0]:res[1]+1]    #如果res始终没被更新过,代表无满足条件的结果

Summary: This type of topic mainly uses the left and right pointers to loop to form a sliding window, and compare the target value t with the sliding window. The main difficulty is how to compare the sliding window with the target value. We use the form of the accounting book to record the required characters as a character integer set need, and subtract 1 every time a target value is met. In this process, we also record each character in the sliding window, these unwanted characters All are recorded as negative values. When they are removed from the sliding window, these characters are recorded as 0. Those chars added to the need from the sliding window, the value of the char that is not the target value will never be positive.

When all the character values ​​in need are less than or equal to 0, the demand is satisfied. The function of maintaining an additional variable needCnt is to record the total number of required elements. When we encounter a required element c, not only the number of need[c] is reduced by 1, but also needCnt is reduced by 1, so we pass needCnt can know whether the conditions are met, so there is no need to traverse the need dictionary.
As mentioned earlier, need records all the elements traversed, the value of char that is not the target value will never be positive, and only when need[c]>0, it means that c is the required element, when all elements in need When the value of char is 0, it means that all target values ​​are satisfied and there are no extra characters in the sliding window.
Link: https://leetcode-cn.com/problems/minimum-window-substring/solution/tong-su-qie-xiang-xi-de-miao-shu-hua-dong-chuang-k/

Another reference:

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        need = collections.defaultdict(int)
        for c in t:
            need[c] += 1
        needCnt = len(t)
        i = 0 #记录起始位置
        res = (0, float('inf'))  #用两个元素,方便之后记录起终点
        #三步骤:
        #1. 增加右边界使滑窗包含t
        for j,c in enumerate(s):
            if need[c] >0:
                needCnt -= 1
            need[c] -= 1 #这行放在外面不可以,看19行 need[c] == 0
        #2. 收缩左边界直到无法再去掉元素   !注意,处理的是i
            if needCnt == 0:
                while True:
                    c = s[i]
                    if need[c] == 0: #表示再去掉就不行了(need>0)
                        break
                    else:
                        need[c] += 1
                        i += 1
                if j-i < res[1] - res[0]:  #这里是否减一都可以,只要每次都是这样算的就行,反正最后也是输出子串而非长度
                    res = (i,j)
        #3. i多增加一个位置,准备开始下一次循环(注意这步是在 needCnt == 0里面进行的 )
                need[s[i]] += 1
                needCnt += 1    #由于 移动前i这个位置 一定是所需的字母,因此NeedCnt才需要+1
                i += 1
        return "" if res[1]>len(s) else s[res[0]: res[1]+1]

 

Guess you like

Origin blog.csdn.net/guyu1003/article/details/107407641