我正在参加「掘金·启航计划」
最小覆盖子串
题目描述
题目分析
这道题目是典型的滑动窗口解决题目。 我们可以通过两个步骤来解决
- 定义两个指针。通过改变指针的位置来寻找所有子串。
- 在子串中找出长度最小的子串
注意题目最后一句。此题目的解法需要将时间复杂度控制在O(n)内 大家注意这个t字符串里面是可以有重复的值出现的。这个时候。选择一种合适的数据结构来存储字符串t和t的数量。尤为重要。
什么样的数据结构既能存储字符串的长度。字符串内每个字符出现的个数。还能动态改变字符串数量?
es6中有一种结构是字典。我们可以通过字典来存储t字符串。
比如t="a,b,c,d,a"
a->2 ,b->1, c->1, d->1
let tMap = new Map()
for (let i in t) {
tMap.set(t[i], tMap.has(t[i]) ? tMap.get(t[i]) + 1 : 1)
}
let tSize = tMap.size
这样就完成对t字符串的存储。接下来。我们就可以开始解题了。
解题步骤
- 定义两个指正 l和r,都指向s字符串的0位置。
- 从0开始移动r的坐标。如果遇到tMap中存储的key。则动态减少此key对应的值。可以减少到负数。
- 如果tMap中的key减少到0。则将tSize对应减少1。
- 当tSize减少到0。则找到一个子串。
while (r < s.length ) {
if (tMap.has(s[r]) ) {
tMap.set(s[r], tMap.get(s[r]) - 1)
if (tMap.get(s[r]) === 0) tSize--
}
r++
}
这个时候我们已经找到第一个子串了。r的坐标可以暂时锁定。开始移动l坐标。
- l坐标从零开始移动。
- tMap中有l坐标对应的key。则操作tMap数据。当tMap中某一项值为1,tSize++
这个时候,我们将会把l坐标,移动到第一个不符合子串的位置。后开始重新移动l坐标。
while (tSize === 0) {
if (tMap.has(s[l])) {
const newRes = s.substring(l, r + 1)
res = !res||newRes.length<res.length?newRes:res
tMap.set(s[l], tMap.get(s[l]) + 1)
if (tMap.get(s[l]) === 1) {
tSize++
}
}
l++
}
循环上述步骤。则会得到所有子串。在子串中找到最小子串,就完美解决该题目。
完整解题代码
/**
* @param {string} s
* @param {string} t
* @return {string}
*/
var minWindow = function (s, t) {
let l = 0,
r = 0,
tMap = new Map(),
res='';
for (let i in t) {
tMap.set(t[i], tMap.has(t[i]) ? tMap.get(t[i]) + 1 : 1)
}
let tSize = tMap.size
while (r < s.length ) {
if (tMap.has(s[r]) ) {
tMap.set(s[r], tMap.get(s[r]) - 1)
if (tMap.get(s[r]) === 0) tSize--
}
while (tSize === 0) {
if (tMap.has(s[l])) {
const newRes = s.substring(l, r + 1)
res = !res||newRes.length<res.length?newRes:res
tMap.set(s[l], tMap.get(s[l]) + 1)
if (tMap.get(s[l]) === 1) {
tSize++
}
}
l++
}
r++
}
return res
}
复杂度
我们解决此题目的 时间复杂度:O(m)+O(n) 空间复杂度:O(m)
其中 m为t字符串的长度 n为s字符串的长度