打卡LeetCode热题100(TS)打卡第九天

83 阅读2分钟

打卡LeetCode热题100(TS版本)

打卡第九天

Problem: 76. 最小覆盖子串

[TOC]

思路

最简单的方式是暴力解法,所谓的暴力解法是遍历出所有长度大于等于t字符串长度的子串,然后让每个子串和t串进行对比。对比方式还是比较特别的,可以采用hash方式来记录每个子串中的字符串,然后判断子串中的每一项是否都在hash中。 暴力破解的时间复杂度在3次方。执行后肯定会超时。由于暴力破解中含有很多重复的操作,我们可以简化。通过观察可以发现我们只需要先从左侧0索引开始先找出最短可以满足t串的子串。满足后我们再把这个子串从左侧开始从右侧缩进,也就是不断的抛弃左侧的元素,直到不能满足t串前停止。然后记录一下当前的子串的起始位置和终止位置。记录后还需要再往右滑动窗口,直到可以出现可以满足t串的子串。

解题方法

我们需要两个hash来记录t字符串中每个元素出现的次数,另外还需要记录在遍历s串时每个字符出现的次数。我们最终统计的s子串中所有出现在t中的字符串结果总和,也就是说如果出现 sMap(s[i]) < tMap(s[i]) 就需要记录一下当前出现在t中的个数总和:validChars。如果出现了 validChars === t.length 意味着该子串满足了t串的要求,并对该串进行缩进,缩进到刚好满足t串的要求,继续往右滑动窗口。

复杂度

  • 时间复杂度:

设字符集是C,则时间复杂度是: O(Cs+t)O(C * s + t)

  • 空间复杂度:

设字符集是C, 则空间复杂度是: O(C)O(C)

Code


function minWindow(s, t) {
  const sLen = s.length;
  const tLen = t.length;
  
  if (sLen === 0 || tLen === 0 || sLen < tLen) {
    return '';
  }

  const sMap = new Map();
  const tMap = new Map();

  for (let i = 0; i < tLen; i++) {
    if (!tMap.has(t[i])) {
      tMap.set(t[i], 0);
    }
    tMap.set(t[i], tMap.get(t[i]) + 1);
  }

  let minLen = Infinity; // Initialize to positive infinity
  let begin = 0;
  let left = 0;
  let right = 0;
  let validChars = 0; // Number of valid characters in the current window

  while (right < sLen) {
    const charR = s[right];

    if (tMap.has(charR)) {
      if (!sMap.has(charR)) {
        sMap.set(charR, 0);
      }

      sMap.set(charR, sMap.get(charR) + 1);

      if (sMap.get(charR) <= tMap.get(charR)) {
        validChars++;
      }
    }

    while (validChars === tLen) {
      if (right - left < minLen) {
        minLen = right - left;
        begin = left;
      }

      const charL = s[left];

      if (tMap.has(charL)) {
        sMap.set(charL, sMap.get(charL) - 1);

        if (sMap.get(charL) < tMap.get(charL)) {
          validChars--;
        }
      }

      left++;
    }

    right++;
  }

  if (minLen === Infinity) {
    return '';
  }

  return s.slice(begin, begin + minLen + 1); // Include the end character in the result
}