返回

最小覆盖子串:JS算法解读与实现

前端

  1. 最小覆盖子串概述

最小覆盖子串是字符串s中包含字符串t所有字符的最小子串,若不存在,则返回空字符串。它在字符串匹配、子串查找和自然语言处理等领域有着广泛的应用。

2. 算法原理

最小覆盖子串的算法原理是基于贪心算法。算法流程如下:

  1. 初始化一个滑动窗口,窗口大小为t的长度。
  2. 从字符串s的第一个字符开始,将字符添加到滑动窗口中,直到窗口中包含t的所有字符。
  3. 移动滑动窗口,将窗口右边界向右移动一位,同时将窗口左边界向右移动一位,直到窗口中不再包含t的所有字符。
  4. 重复步骤2和步骤3,直到窗口右边界到达字符串s的最后一个字符。
  5. 记录下窗口大小最小的子串,即为最小覆盖子串。

3. JS实现

/**
 * 寻找字符串s中包含字符串t所有字符的最小子串
 * @param {string} s
 * @param {string} t
 * @returns {string}
 */
const minWindow = (s, t) => {
  if (s.length < t.length) {
    return "";
  }

  const tMap = {};
  for (let i = 0; i < t.length; i++) {
    const char = t[i];
    tMap[char] = (tMap[char] || 0) + 1;
  }

  let minLen = Infinity;
  let minStart = 0;
  let windowStart = 0;
  let windowEnd = 0;
  let matched = 0;

  while (windowEnd < s.length) {
    const char = s[windowEnd];
    if (char in tMap) {
      tMap[char]--;
      if (tMap[char] >= 0) {
        matched++;
      }
    }

    while (matched === t.length) {
      if (windowEnd - windowStart + 1 < minLen) {
        minLen = windowEnd - windowStart + 1;
        minStart = windowStart;
      }

      const leftChar = s[windowStart];
      if (leftChar in tMap) {
        tMap[leftChar]++;
        if (tMap[leftChar] > 0) {
          matched--;
        }
      }

      windowStart++;
    }

    windowEnd++;
  }

  return minLen === Infinity ? "" : s.substring(minStart, minStart + minLen);
};

4. 算法复杂度分析

算法的时间复杂度为O(s + t),其中s是字符串s的长度,t是字符串t的长度。算法需要遍历字符串s一次,并在每次遍历中检查窗口中是否包含t的所有字符,因此时间复杂度为O(s)。此外,算法还需要对字符串t中的每个字符进行计数,因此时间复杂度为O(t)。因此,算法的总时间复杂度为O(s + t)。

5. 常见面试题

最小覆盖子串问题经常出现在技术面试中,以下是几个常见的面试题:

  • 寻找字符串s中包含字符串t所有字符的最小子串。
  • 寻找字符串s中包含字符串t所有字符的最长子串。
  • 寻找字符串s中包含字符串t所有字符的最少重复次数。

6. 总结

最小覆盖子串算法是一种贪心算法,它可以高效地找到字符串s中包含字符串t所有字符的最小子串。算法原理简单,易于理解和实现。它在字符串匹配、子串查找和自然语言处理等领域有着广泛的应用。通过本文的学习,希望您对最小覆盖子串算法有了更深入的了解。