438. 找到字符串中所有字母异位词(中)

题目

  • 给定两个字符串 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" 的异位词。

题解:滑动窗口

python

class Solution:
    def findAnagrams(self, s: str, t: str) -> List[int]:
        need={}# 存储字符串 t 中各个字符的需求量
        window={}# 存储滑动窗口中各个字符的出现次数
        for c in t:#遍历字符串t
            need.setdefault(c,0)#访问不存在的键时自动创建并将值设置为 0
            need[c]+=1# 统计字符串 t 中各个字符的需求量
        left=0# 滑动窗口的左指针
        right=0# 滑动窗口的右指针
        valid=0# 记录满足需求的字符数
        res=[]#结果列表
        while right<len(s):
            c=s[right]# 当前字符
            right+=1# 右指针右移
            if c in need:#当前字符是目标字符中的
                window.setdefault(c,0)#访问不存在的键时自动创建并将值设置为 0
                window[c]+=1# 更新滑动窗口中当前字符的出现次数
                if window[c]==need[c]:
                    valid+=1# 如果滑动窗口中当前字符的出现次数达到需求量,增加满足需求的字符数
            while valid==len(need):#每个字符的次数都达到了要求
                if right-left==len(t):#数量也是相等的话
                    res.append(left)#把第一个索引的坐标加入结果列表
                d=s[left]# 将要移出窗口的字符
                left+=1# 左指针右移
                if d in need:#当前字符是目标字符中的
                    if window[d]==need[d]:#如果滑动窗口中当前字符等于目标字符的值
                        valid-=1# 如果移出窗口的字符导致窗口不再满足需求,则减少满足需求的字符数
                    window[d]-=1# 更新滑动窗口中移出字符的出现次数
        return res#返回结果列表

javascript

  • 思路:要在s中找到字符串p的异位词,相当于对s按固定长度的窗口滑动,窗口内的元素与s中元素出现的次数相等就把窗口左指针下标加入结果数组
var findAnagrams = function(s, p) {
    const sLen = s.length, pLen = p.length; // 获取s和p的长度
    if (sLen < pLen) { // 如果s的长度小于p的长度,返回空数组
        return [];
    }
    const ans = []; // 结果数组,用于存储找到的起始索引
    const sCount = new Array(26).fill(0); // 初始化s中每个字母的计数数组
    const pCount = new Array(26).fill(0); // 初始化p中每个字母的计数数组

    // 记录p的每个字母出现次数,同时记录s的前pLen个字母出现次数
    for (let i = 0; i < pLen; ++i) {
        //s[i]的ASCII码减去a的ASCII码的值作为sCount索引
        ++sCount[s[i].charCodeAt() - 'a'.charCodeAt()]; // 统计s中当前字母出现次数
        ++pCount[p[i].charCodeAt() - 'a'.charCodeAt()]; // 统计p中当前字母出现次数
    }
    
    // 如果s的前pLen个字母与p的字母出现次数相同,则记录起始索引0
    //下面循环窗口已经向右滑动,滑动前就应该正确处理第一个窗口
    if (sCount.toString() === pCount.toString()) {
        ans.push(0);
    }

    // 从s的pLen到sLen遍历,检查每个窗口的字母计数
    for (let i = 0; i < sLen - pLen; ++i) {
        // 移动窗口:减少当前字母的计数,增加新字母的计数
        --sCount[s[i].charCodeAt() - 'a'.charCodeAt()]; // 减去s中当前字母的计数
        ++sCount[s[i + pLen].charCodeAt() - 'a'.charCodeAt()]; // 增加s中下一个字母的计数

        // 检查当前窗口的字母计数是否与p的字母计数相同
        if (sCount.toString() === pCount.toString()) {
            ans.push(i + 1); // 记录当前窗口的起始索引(i + 1)
        }
    }

    return ans; // 返回所有找到的起始索引
};
posted @   Frommoon  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
点击右上角即可分享
微信分享提示