题目来源:https://leetcode.com/problems/minimum-window-substring/
问题描述
76. Minimum Window Substring
Hard
Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).
Example:
Input: S = "ADOBECODEBANC", T = "ABC"
Output: "BANC"
Note:
- If there is no such window in S that covers all characters in T, return the empty string "".
- If there is such window, you are guaranteed that there will always be only one unique minimum window in S.
------------------------------------------------------------
题意
给定母串s和模式串t,求s串的最短子串sub,可以使得t中出现的每个字符在sub中都出现了,且sub中这些字符出现的次数 >= t中这些字符出现的次数。(定义这种sub与t的关系为sub“包含”t)要求O(n)时间复杂度。
------------------------------------------------------------
思路
滑窗典型题。滑窗题的套路是维护两个指针left和 right。类似贪心法,首先固定left,right不断右移直到当前的s[left:right]包含t,然后再将left指针右移找到当前right指针位置下最短的包含t的子串,找到这样的子串后left再前进一格,破坏包含关系,再次固定left,right向右滑动。right和left各自遍历了一遍母串s,假设母串长度为n,滑窗算法的时间复杂度为O(2n)=O(n).
在实现上,一个自然的想法是用两个hashmap<char, int>,一个tmap记录模式串t中的字符和字符出现的次数,一个smap动态记录当前子串中字符和字符出现的次数。但是这样判断“包含”关系要判断对于tmap的每条记录,smap是否有对应记录且是否>=tmap中的记录,这样每次判断会产生O(T)的时间(其中T为模式串t的长度),而这样的判断会进行n次,总时间复杂度就变成O(nT)了。观察这个问题,主要的时间开销在两个map的键值比较上,为此我们考虑省掉smap,动态改变tmap来判断“包含”关系。另外,引入int变量cnt,当cnt=0时,表示“包含”关系成立。具体而言,首先用t的字符初始化tmap,用t的长度初始化cnt. 在循环过程中,right指针每移动一次读取母串s的一个char, tmap会把char对应的键值的value-1,如果原先char对应的value>0,说明char在t中,所以表征匹配数的cnt-1; 如果cnt=0,表示包含关系成立,固定right指针,移动left指针;left指针每移动一次读取母串s的一个char,tmap会把char对应的键值的value+1,如果+1之后char对应的value>0,说明char在t中,所以表征匹配数的cnt+1,此时固定left指针,移动right指针。如此循环。
------------------------------------------------------------
代码
func minWindow(s string, t string) string {
tmap := make(map[byte]int)
for i,m:=0,len(t); i<m; i++ {
tmap[t[i]]++
}
n, l, r, cnt := len(s), 0, 0, len(t)
minLen, minL := n + 1, -1
for r<n {
if tmap[s[r]]>0 {
cnt--
}
tmap[s[r]]--
for cnt == 0 {
if r - l + 1 < minLen {
minLen = r - l + 1
minL = l
}
tmap[s[l]]++
if tmap[s[l]] > 0 {
cnt++
}
l++
}
r++
}
if minL == -1 {
return ""
} else {
return s[minL:minL+minLen]
}
}