找到字符串中所有字母异位词

94 阅读1分钟

这是我参与「掘金日新计划 · 2 月更文挑战」的第 23 天,点击查看活动详情

问题描述

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

示例 1:

输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。

示例 2:

输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。

提示:

  • 1 <= s.length, p.length <= 3 * 10^4
  • s 和 p 仅包含小写字母

思路分析

首先我们先要理解一下题目意思,题目会给我们两个字符串,sp,我们需要在 s 中找出所有 p 的异位词 的子串,这里异位词的意思是指由相同字母重排列形成的字符串(包括相同的字符串)。如abc的异位词有:abcacbbacbcacabcba

所以我们只需要统计两个字符串中的每个字符数量就可以判断是不是异位词,这道题我们可以使用双指针的方法来解题,遍历一次s字符串,统计遍历过的每个字符出现的次数,遇到没有在p字符串中出现过的字符时,我们需要移动指针到当前遍历到的下一个位置;如果当前遍历到的字符出现次数比在p字符串中出现的次数更多,我们需要将指针不断向右移,直到当前字符出现的次数与p字符串中出现的次数一样多。如果当前遍历到的下标与指针所指下标之差为p字符串长度,则说明指针所指下标到当前遍历到的位置之间存在一个异位词,我们可以将指针所指下标存入结果集合中去。

  • 如果当前字符不在p字符串中,说明包含当前字符的不可能为p字符串的异位词,需要重置指针位置。
if (!map[s[i]]) {
  left = i + 1;
  tmap = {};
}
  • 如果当前遍历到的字符出现次数比在p字符串中出现的次数更多,我们需要将指针不断向右移,直到当前字符出现的次数与p字符串中出现的次数一样多。
else {
    while (tmap[s[i]] > map[s[i]]) {
        tmap[s[left++]]--;
    }
}
  • 如果当前遍历到的下标与指针所指下标之差为p字符串长度,则说明指针所指下标到当前遍历到的位置之间存在一个异位词,我们可以将指针所指下标存入结果集合中去。
if (i - left + 1 == p.length) {
  res.push(left);
}

完整 AC 代码如下:

AC 代码

/**
 * @param {string} s
 * @param {string} p
 * @return {number[]}
 */
var findAnagrams = function (s, p) {
  const map = {};
  for (const ch of p) {
    map[ch] = (map[ch] || 0) + 1;
  }
  let tmap = {};
  let left = 0,
    res = [];
  for (let i = 0; i < s.length; i++) {
    tmap[s[i]] = (tmap[s[i]] || 0) + 1;
    if (!map[s[i]]) {
      left = i + 1;
      tmap = {};
    } else {
      while (tmap[s[i]] > map[s[i]]) {
        tmap[s[left]]--;
        left++;
      }
    }
    if (i - left + 1 == p.length) {
      res.push(left);
    }
  }
  return res;
};

说在后面

本人为算法业余爱好者,平时只是随着兴趣偶尔刷刷题,如果上面分享有错误的地方,欢迎指出,感激不尽。