算法题——找到字符串中所有字母异位词

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

示例 1:

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

示例 2:

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

提示:

  • 1 <= s.length, p.length <= 3 * 104
  • sp 仅包含小写字母

题解:滑动窗口

  • 我们需要检查 s 中长度为 p.length 的子串,是否是 p 的异位词。
  • 所谓异位词:两个字符串的字母出现次数完全一致。
  • 所以我们可以用 频次数组/哈希表 来比较子串与 p 的字母频率。

滑动窗口

  • 用两个数组 needwindow 统计字母出现次数。
  • 窗口大小固定为 p.length,右指针扩展窗口,左指针收缩窗口。
  • 每次窗口长度等于 p.length 时,比较两个数组是否一致,如果一致就把 left 加入结果。

比较两个数组是否相等可以通过记录"匹配的字符个数"来优化,而不是每次都遍历 26 个字母。

js 复制代码
var findAnagrams = function(s, p) {
  const sLen=s.length,pLen=p.length
  const res=[]
  if(sLen<pLen) return []
  
  const sCount=new Array(26).fill(0)
  const pCount=new Array(26).fill(0)
  for(let i=0;i<pLen;i++){
    sCount[s[i].charCodeAt()-'a'.charCodeAt()]++
    pCount[p[i].charCodeAt()-'a'.charCOdeAt()]++
  }
  if(sCount.toString()===pCount.toString()){
    res.push(0)
  }
  
  for(let i=0;i<sLen-pLen;i++){
    sCount[s[i].charCodeAt()-'a'.charCodeAt()]--
    sCount[s[i+pLen].charCodeAt()-'a'.charCodeAt()]++
    
    if(sCount.toString()===pCount.toString()){
      res.push(i+1)
    }
  }
  
  return res
  
}

优化后的滑动窗口(有点难理解,记住上面的方法也行)

用一个数组 count 表示每个字母的差异:

  • count[i] > 0:窗口里这个字母比 p
  • count[i] < 0:窗口里这个字母比 p
  • count[i] === 0:这个字母数量相同

用一个变量 differ 来记录 有多少个字母的数量不相等

  • differ === 0 → 当前窗口正好是 p 的异位词
js 复制代码
/**
 * @param {string} s
 * @param {string} p
 * @return {number[]}
 */
var findAnagrams = function(s, p) {
    const sLen = s.length, pLen = p.length;

    if (sLen < pLen) {
        return [];
    }

    const ans = [];
    const count = Array(26).fill(0);
    for (let i = 0; i < pLen; ++i) {
        ++count[s[i].charCodeAt() - 'a'.charCodeAt()];
        --count[p[i].charCodeAt() - 'a'.charCodeAt()];
    }

    let differ = 0;
    for (let j = 0; j < 26; ++j) {
        if (count[j] !== 0) {
            ++differ;
        }
    }

    if (differ === 0) {
        ans.push(0);
    }

    for (let i = 0; i < sLen - pLen; ++i) {
        if (count[s[i].charCodeAt() - 'a'.charCodeAt()] === 1) {
            --differ;
        } else if (count[s[i].charCodeAt() - 'a'.charCodeAt()] === 0) { 
            ++differ;
        }
        --count[s[i].charCodeAt() - 'a'.charCodeAt()];

        if (count[s[i + pLen].charCodeAt() - 'a'.charCodeAt()] === -1) { 
            --differ;
        } else if (count[s[i + pLen].charCodeAt() - 'a'.charCodeAt()] === 0) { 
            ++differ;
        }
        ++count[s[i + pLen].charCodeAt() - 'a'.charCodeAt()];

        if (differ === 0) {
            ans.push(i + 1);
        }
    }

    return ans;
};

时间复杂度O(1)

相关推荐
颂love8 分钟前
Vue的两大生态以及组件通信
前端·javascript·vue.js·typescript
洛水水23 分钟前
【力扣100题】76.搜索插入位置
数据结构·算法·leetcode
光影少年23 分钟前
js单线程,为什在node环境下的js可以处理高并发请求?
前端·javascript·掘金·金石计划
Techblog of HaoWANG27 分钟前
智巡守卫:多模态巡检智能体算法服务端设计与实现——基于Ollama+Qwen3.5的自动化巡检报告生成系统
运维·人工智能·算法·目标检测·自动化·边缘计算
小蒋学算法37 分钟前
算法-灌溉花园的最少龙头数目-贪心
算法
满怀冰雪37 分钟前
第07篇-差分算法-高效处理区间修改问题
数据结构·算法
moMo39 分钟前
# JavaScript 的“等等我”:聊聊同步与异步
javascript
KaMeidebaby40 分钟前
卡梅德生物技术快报|重组蛋白的表达和纯化:工艺调试全记录:大肠杆菌体系重组蛋白的表达和纯化参数标定(肠激酶轻链案例)
前端·人工智能·算法·数据挖掘·数据分析
Cobyte1 小时前
19.Vue Vapor 的实现原理原来这么简单
前端·javascript·vue.js
JackieDYH1 小时前
uniapp vue3 常用的生命周期和作用使用时机
javascript·vue.js·uni-app