题目
LeetCode.76.最小覆盖子串(困难)
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
- 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
- 如果 s 中存在这样的子串,我们保证它是唯一的答案。
进阶:你能设计一个在 o(n)
时间内解决此问题的算法吗?
题解
暴力朴素法
设s
长度为n
,t
长度为m
。
把t
的字符都存在哈希表中,相同字符进行计数。
从s
的每一个位置开始,看后面的m
个字符是否都在哈希表中,并且相同字符的个数也一样。
这样匹配的时间复杂度是O(mn)
,空间复杂度O(m)
。
滑动窗口
暴力法有很多重复的判断,所以时间复杂度高,如果能把已经判断过的东西不用再判断了就可以降低时间复杂度。
可以使用滑动窗口,最多也就是滑动窗口的左右边界走完整个字符串,时间复杂度是O(n)
的。
算法步骤:
- 不停的扩大窗口,直至窗口中包含了t中所有字符。
- 不停的缩小窗口,直至窗口中没有包含t中所有字符。
- 重复上述两步,每一步记录最小的窗口的大小。
还是需要把t
的字符都存在哈希表中,方便判断窗口中有没有包含t中所有字符,相同字符进行计数。
细节见源码注释。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| class Solution {
fun minWindow(s: String, t: String): String { var minSubstr = "" val window = Window(s, t) while (window.extend()) { while (window.match()) { if (minSubstr.isEmpty() || window.windowSize() < minSubstr.length) { minSubstr = window.windowString() } window.shorten() } } return minSubstr }
class Window( private val s: String, private val t: String ) { private val tMap = mutableMapOf<Char, Int>()
private val wMap = mutableMapOf<Char, Int>()
private var matchedCharCount = 0
private var left = 0
private var right = -1
init { t.forEach { c -> tMap[c] = 1 + tMap.getOrDefault(c, 0) } }
fun extend(): Boolean { if (right == s.length - 1) return false
right++
val c = s[right]
if (tMap.containsKey(c)) { wMap[c] = 1 + wMap.getOrDefault(c, 0)
if (tMap.containsKey(c) && tMap[c] == wMap[c]) { matchedCharCount++ } }
return true }
fun match(): Boolean { return matchedCharCount == tMap.size }
fun shorten() { val c = s[left]
if (tMap.containsKey(c)) { wMap[c] = wMap.getOrDefault(c, 0) - 1 if (wMap[c]!! < tMap[c]!!) { matchedCharCount-- } }
left++ }
fun windowSize(): Int { return right - left + 1 }
fun windowString(): String { return s.substring(left, right + 1) } } }
|