0076-leeycode算法实现之最小覆盖子串-minimum-window-substring-python&golang实现

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

注意:

对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
示例 2:

输入:s = "a", t = "a"
输出:"a"
示例 3:

输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

提示:

1 <= s.length, t.length <= 105
s 和 t 由英文字母组成

进阶:你能设计一个在 o(n) 时间内解决此问题的算法吗?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-window-substring

python

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        """
        滑动窗口,时间:右指针遍历一次s串,左指针最多遍历一次完整串,即O(2n)->O(n),空间:有限变量+s与t共有的元素O(k),常量级即O(1)
        思路:
        1.need哈希表记录t中元素及出现次数,needCnt表示t中元素长度或个数,通过这两个变量可以知道加入的元素,没有加入的元素,就不需要每次窗口遍历need获取哪些元素还需加进窗口
          在遍历s的过程中,遍历的元素,每次遍历到的value--,即s-t的元素value都是负数,0表示刚好没有,而 s&t的元素,小于0多余,等于0刚好,大于0需要移动右指针扩大窗口
        2.移动右指针,扩大窗口,加入新元素
        3.移动左指针,缩小窗口,寻找当前窗口的最小
        4.当左指针已经是当前右界的最小窗口的左界时,右移左指针,寻找下一个符合条件的窗口
        算法:
        1.初始化准备工作:
        -初始化need哈希表,value=0,遍历t,记录字符及个数,
        -needCnt初始化为当前t的长度,
        -初始化左指针i在0处,
        -初始化返回结果res,右上下界组成,初始化为0到无穷大
        2.右指针j右移
        -优先判断是否遇到t中元素,是则needCnt--
        -need对应字符的value--
        -检查当前窗口是否覆盖t中元素,进入3步骤
        3.needCnt==0表示当前窗口覆盖t中元素,可以通过左指针右移缩小窗口以获得局部最小覆盖窗口
        -检查当前左指针字符,如果need中字符value为0,即当前为局部最小最优解,跳出循环
            -不等于0,即小于0,窗口可以优化,need中value++,左指针右移,重复上面及此步骤
        -已经是局部最优解,检查与res的大小,取较小值记录,每次记录都应该在左窗口右移缩小时记录
        -寻找下一个符合条件的窗口,然后优化窗口寻找局部最优
            -当前元素的need value++,needCnt++,左指针i++右移
        重复以上步骤
        4.如果没有找到res即res还是初始化的上下界,返'',否则返回最小覆盖子串
        """
        from collections import defaultdict
        need=defaultdict(int) # 初始化哈希表,用于存储遍历的窗口元素及出现次数
        for c in t: # 遍历字符t,遇到字符即+1,遍历完即为t中所有元素出现次数的统计
            need[c]+=1
        needCnt=len(t) # needCnt用于统计遍历窗口中是否全部包含了t的元素
        i=0 # 初始化窗口左边界指针,窗口右减左加
        res=(0,float('inf')) # 初始化返回结果,0 到 无穷大
        for j,c in enumerate(s): # 枚举或遍历字符串s, j为下标,c为字符
            if need[c]>0: # 检查字符c次数是否大于0,即遇到t中元素,needCnt--,True表示窗口可以继续右移,j++
                needCnt-=1 # 由于只有在t中的元素出现在s中,其value才为正,needCnt--表示此次出现一次,其他非t中字符出现在s中,不进此逻辑判断
            need[c]-=1 # 不管遇到什么字符,遍历到就--,非t中元素出现在s中,肯定是多余,也一定是负数
            if needCnt==0:       #步骤一:滑动窗口包含了所有T元素,>>> 由于neddCnt表示t中字符长度或个数,如果为0,表示当前窗口下,已经包含有所有t中字符,否则j++
                while True:      #步骤二:增加i,排除多余元素 >>> 通过不断地右移左指针,缩小当前窗口长度,排除非t中字符
                    c=s[i] # 当前在s中的窗口左指针字符
                    if need[c]==0: # 检查左指针字符对应的need哈希value是否等于0,不等于0,肯定小于0,多余字符,可右移缩小窗口,假设移动了该字符,由于是左指针,相当于减小窗口,need[c]++,如果大于0,说明窗口还需要扩大,但右指针当前固定为j,所以当前窗口可以作为临时合要求的结果
                        break # 和符合要求的窗口,应该退出左指针右移
                    need[c]+=1 # 如果不等于0,说明左指针指向的字符是多余字符,对应字符value++
                    i+=1 # 上面移动左指针因为是多余字符,可以继续右移左指针, i++
                if j-i<res[1]-res[0]:   #记录结果 >>> 只要小都应该记录,而且更新,相当于更新窗口长度,同时也是固定了位置
                    res=(i,j)
                need[s[i]]+=1  #步骤三:i增加一个位置,寻找新的满足条件滑动窗口 >>> 当前窗口是包含t中元素的最小窗口,后面左指针元素的need的value++,表示左指针字符次数++,表示需要加入的t中元素
                needCnt+=1 # 肯定有某个t中的元素需要在新窗口中加入
                i+=1 # 右移左指针,寻找新的符合条件的窗口
        return '' if res[1]>len(s) else s[res[0]:res[1]+1] #如果res始终没被更新过,代表无满足条件的结果 >>> 如果res没有更新,res[1] = inf,无穷大,就是没有找到合适的窗口,否则返回最小的子串

if __name__ == "__main__":
    s = 'usyiskbleasydknitehw'
    t = 'sky'
    test = Solution()
    print(test.minWindow(s,t))

golang

package main

import "fmt"

func main()  {
	s := "ADOBECODEBANC"
	t := "ABC"
	fmt.Println(minWindow(s,t))
}

func minWindow(s string, t string) string {
	ori, cnt := map[byte]int{}, map[byte]int{}
	for i := 0; i < len(t); i++ {
	    ori[t[i]]++
	}
  
	sLen := len(s)
	len := math.MaxInt32
	ansL, ansR := -1, -1
  
	check := func() bool {
	    for k, v := range ori {
		  if cnt[k] < v {
			return false
		  }
	    }
	    return true
	}
	for l, r := 0, 0; r < sLen; r++ {
	    if r < sLen && ori[s[r]] > 0 {
		  cnt[s[r]]++
	    }
	    for check() && l <= r {
		  if (r - l + 1 < len) {
			len = r - l + 1
			ansL, ansR = l, l + len
		  }
		  if _, ok := ori[s[l]]; ok {
			cnt[s[l]] -= 1
		  }
		  l++
	    }
	}
	if ansL == -1 {
	    return ""
	}
	return s[ansL:ansR]
原文地址:https://www.cnblogs.com/davis12/p/15418217.html