1. 题目描述

给你一个字符串 S、一个字符串 T 。请你设计一种算法,可以在 O(n) 的时间复杂度内,从字符串 S 里面找出:包含 T 所有字符的最小子串。

示例:

  1. 输入:S = "ADOBECODEBANC", T = "ABC"
  2. 输出:"BANC"

提示:

  • 如果 S 中不存这样的子串,则返回空字符串 “”。
  • 如果 S 中存在这样的子串,我们保证它是唯一的答案。

    2. 解题思路

    这题目可以使用双指针+map实现。

  • 首先,定义一个map,将子串所需要的字符及其个数存贮在map中

  • 定义两个指针,来维护一个滑动窗口
  • 右指针往后移动,如果发现当前的右指针的值在map中存在,就map中该元素需要的个数减一,如果该元素需要的个数为0,那总需要的个数needType也减一
  • 如果需要的元素个数为0(也就是左右指针之间已经包含了所需字符),那么就向右移动左指针来缩小范围,同时如果map中需要该元素,就往其中加一,需求数也加一。
  • 在缩小范围的同时,截取两指针之间的字符串,找出最短的字符串即可

该算法的时间复杂度为O(m+n),m和n分别是两个字符串的长度,所以符合题目要求的O(n)的时间复杂度。空间复杂度是O(m),m是s字符串的长度。

3. 代码实现

  1. /**
  2. * @param {string} s
  3. * @param {string} t
  4. * @return {string}
  5. */
  6. var minWindow = function(s, t) {
  7. let l = 0
  8. let r = 0
  9. const need = new Map() // 表示子串的字符及个数
  10. for(let i of t){
  11. need.set(i, need.has(i) ? need.get(i)+1 : 1 )
  12. }
  13. let needtype= need.size
  14. let res = ''
  15. // 移动右指针
  16. while(r< s.length){
  17. const c= s[r]
  18. // 如果右指针当前的值在子串中存在,则need中该字符数也-1
  19. if(need.has(c)){
  20. need.set(c, need.get(c) - 1)
  21. if(need.get(c) === 0) needtype -= 1 //如果这个字符需要数为0,则需要的个数为-1
  22. }
  23. // 子串已包含所有需要的字符,用该循环来缩小字串的长度
  24. while(needtype === 0){
  25. // 截取子串,找出最小长度的子串
  26. const newRes = s.substring(l, r+1)
  27. if(!res || newRes.length < res.length) res = newRes
  28. const c2 = s[l]
  29. // 移动左指针,如果左指针在需要的子串里面,需求数就+1
  30. if(need.has(c2)){
  31. need.set(c2, need.get(c2)+ 1 )
  32. if(need.get(c2) === 1) needtype += 1
  33. }
  34. l += 1
  35. }
  36. r += 1
  37. }
  38. return res
  39. };

4. 提交结果

76. 最小覆盖子串 - 图1