剑指 Offer II 017. 含有所有字符的最短字符串(滑动窗口模块)

185 阅读1分钟

每日刷题第9天 2021.1.4

含有所有字符的最短字符串

  • 难度:困难
  • 方法:双指针

题目

  • 给定两个字符串 s 和 t 。返回 s 中包含 t 的所有字符的最短子字符串。如果 s 中不存在符合条件的子字符串,则返回空字符串 "" 。
  • 如果 s 中存在多个符合条件的子字符串,返回任意一个。
  • 注意: 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。

示例

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC" 
解释:最短子字符串 "BANC" 包含了字符串 t 的所有字符 'A''B''C'
输入: s = "a", t = "a"
输出: "a"
输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,因此没有符合条件的子字符串,返回空字符串。

提示

  • 1 <= s.length, t.length <= 105
  • s 和 t 由英文字母组成

解法

/**
 * @param {string} s
 * @param {string} t
 * @return {string}
 */
var minWindow = function(s, t) {
  let map1 = new Map();
  // 处理数据
  for (let i = 0; i < t.length; i++) {
    if (map1.has(t[i])) {
      map1.set(t[i], map1.get(t[i]) + 1);
    }else {
      map1.set(t[i], 1);
    }
  }
  // console.log('map1', map1);
  // 开始指针
  let start = 0;
  // 结束指针
  let end = 0;
  // 新的记录集合
  let map2 = new Map();
  // 存储找到的符合的字符的长度
  let len = 0;
  // 记录成功的数据集合
  let str = '';
  // 判断长度
  let minLen = Infinity;
  // 记录最终结果
  let ans = -1;
  while (end < s.length) {
    // 满足
    if (map1.has(s[end])) {
      // 记录下来
      if (map2.has(s[end])) {
        map2.set(s[end], map2.get(s[end]) + 1);
      }else {
        map2.set(s[end], 1);
      }
      // console.log('start',start,'end',end,'2end',map2.get(s[end]),'1end',map1.get(s[end]));
      // len长度++,只有当map1中的值大于等于map2中的值
      if (map2.get(s[end]) <= map1.get(s[end])) {
        len++;
      }
      // console.log('len', len);
      // 判断len的长度,是否达到标准,必定相同
      if (len == t.length) {
        // console.log('达到标准',len,'start',start,'end',end);
        // 从start 到 end 存储成一个字符串进行输出
        if (minLen >= (end - start + 1)) {
          ans = start;
          minLen = end - start + 1;
        }
        // 处理start,让start寻找打破标准后停止
        while (start <= end) {
          // console.log('minLen', minLen);
          // 包含有start
          if (map1.has(s[start])) {
            map2.set(s[start], map2.get(s[start]) - 1);
            // console.log('-1',map2.get(s[start]));
          }else {
            start++;
            if (minLen >= (end - start + 1)) {
              ans = start;
              minLen = end - start + 1;
            }
            continue;
          }
          // 判断长度是否需要减少,打破了平衡标准
          if (map2.get(s[start]) < map1.get(s[start])) {
            // console.log('m2',map2.get(s[start]),'m1',map1.get(s[start]));
            len--;
            start++;
            break;
          }else {
            start++;
            if (minLen >= (end - start + 1)) {
              ans = start;
              minLen = end - start + 1;
            }
          }
        }
      }
      end++;
    }else {
      // 不满足
      end++;
    }
  }
  if (ans == -1) {
    return '';
  }else {
    let str = '';
    for (let i = ans; i < ans + minLen; i++) {
      str += s[i];
    }
    return str;
  }
};

附录

  • 双指针
  • 思路对,还要将伪代码写出来