1. // 76. 最小覆盖子串
    2. // 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
    3. //
    4. // 注意:
    5. // 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
    6. // 如果 s 中存在这样的子串,我们保证它是唯一的答案。
    7. //
    8. // 示例 1:
    9. // 输入:s = "ADOBECODEBANC", t = "ABC"
    10. // 输出:"BANC"
    11. // 示例 2:
    12. // 输入:s = "a", t = "a"
    13. // 输出:"a"
    14. // 示例 3:
    15. // 输入: s = "a", t = "aa"
    16. // 输出: ""
    17. // 解释: t 中两个字符 'a' 均应包含在 s 的子串中,
    18. // 因此没有符合条件的子字符串,返回空字符串。
    19. /**
    20. * @param {string} s
    21. * @param {string} t
    22. * @return {string}
    23. */
    24. // 题解
    25. // 找出所有子串
    26. // 找出最短子串
    27. // 双指针维护一个滑动窗口
    28. var minWindow = function(s, t) {
    29. // 定义左右指针或者叫做快慢指针,起始都是从零开始的
    30. let l = 0;
    31. let r = 0;
    32. // 创建一个集合用来存储t,键为item, 值为item出现的次数
    33. let map = new Map();
    34. for(let item of t) {
    35. map.set(item, map.get(item) ? map.get(item) + 1 : 1)
    36. }
    37. let mapType = map.size;
    38. let res = '';
    39. // 遍历S
    40. while(r < s.length) {
    41. const c = s[r];
    42. // 如果包含当前值,就更新map
    43. if(map.has(c)) {
    44. map.set(c, map.get(c) - 1);
    45. // 什么时候停止呢?当map的value 小于等于0时,这里我们不用再去遍历
    46. if(map.get(c) === 0) {
    47. mapType -= 1
    48. }
    49. }
    50. // 左指针怎么移动,
    51. // 左指针什么时候移动呢?就是当我们找到在S中找到了所有t,也就是mapType === 0的时候
    52. while(mapType === 0) {
    53. const newRes = s.substring(l, r+1);
    54. if(!res || newRes.length < res.length) {
    55. res = newRes
    56. }
    57. // 获取左指针当前值
    58. const c2 = s[l];
    59. // 如果左指针存在于map中,给value + 1
    60. if(map.has(c2)) {
    61. map.set(c2, map.get(c2) + 1);
    62. if(map.get(c2) === 1) {
    63. mapType += 1
    64. }
    65. }
    66. l += 1
    67. }
    68. r += 1;
    69. }
    70. return res
    71. };
    72. let s = "ADOBECODEBANC", t = "ABC";
    73. console.log(minWindow(s, t));

    时间复杂度:O(M + N) m 就是t的长度, n 就是s长度
    空间复杂度:新建了一个字典,最坏的情况就是t的长度, O(k) k就是t里面不同字符的个数