【Day 3】公开打卡第3天 | LeetCode 438. 找到字符串中所有字母异位词 + Go 滑动窗口模板

5 阅读3分钟

【Day 3】公开打卡第3天 | LeetCode 438. 找到字符串中所有字母异位词 + Go 滑动窗口模板

正文结构

  1. 今日打卡宣言 Day 3,继续坚持!昨天做了最长回文子串,今天换一道滑动窗口高频题。项目忙归忙,但回家后还是挤出时间手写了一遍。连续3天没断,感觉越来越有动力了,欢迎继续监督!

  2. LeetCode 部分

    • 题目链接:leetcode.cn/problems/fi…
    • 难度:中等
    • 时间复杂度:O(n)(n = s 长度)
    • 核心思路(简短3行): 用固定长度窗口(等于 p 的长度)在 s 上滑动。维护两个计数器(p 的字符频次 + 当前窗口频次)。当窗口频次完全匹配 p 时,记录起始索引。滑动时只更新移出/移入的字符,高效 O(26) 判断匹配。
    • 代码(Go 版本,标准滑动窗口 + 数组计数器,更快):

Go

func findAnagrams(s string, p string) []int {
    if len(p) > len(s) {
        return []int{}
    }
    
    var pCount [26]int
    var winCount [26]int
    for _, ch := range p {
        pCount[ch-'a']++
    }
    
    var result []int
    // 初始化第一个窗口
    for i := 0; i < len(p); i++ {
        winCount[s[i]-'a']++
    }
    if winCount == pCount {
        result = append(result, 0)
    }
    
    // 滑动窗口
    for i := len(p); i < len(s); i++ {
        // 移出左边字符
        winCount[s[i-len(p)]-'a']--
        // 移入右边字符
        winCount[s[i]-'a']++
        
        if winCount == pCount {
            result = append(result, i-len(p)+1)
        }
    }
    
    return result
}
  • C++ 版本:

C++

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        if (p.size() > s.size()) return {};
        // 相当于是, 固定滑窗, 并且判断窗口内是否相同
        vector<int> res;

        array<int, 26> pCnt = {}; array<int, 26>windowCnt = {};

        // 先初始化第0个
        for (char c : p) pCnt[c - 'a'] ++;

        for (int i = 0; i < p.size(); i++)
        {
            windowCnt[s[i] - 'a'] ++;
        }

        if (pCnt == windowCnt) {
            res.emplace_back(0);
        }

        // 接之后的窗口
        for (int i = p.size(); i < s.size(); i++)
        {
            // 左边的移出来
            windowCnt[s[i - p.size()] - 'a']--;
            // 当前的加进来
            windowCnt[s[i] - 'a'] ++;

            if (windowCnt == pCnt)
            {
                res.emplace_back(i - p.size() + 1);
            }
        }
        return res;
    }
};
  • 易错点/优化点:

    1. 窗口长度必须固定为 len(p),别用可变窗口。
    2. 用 [26]int 数组比 map 快得多(因为小写字母限定)。
    3. 数组直接 == 比较(Go/C++ 支持),不用每次循环26个。
    4. 面试追问:如果字符集更大?(用 map,但 O(n log 26) 变 O(n * 26))。示例:s = "cbaebabacd", p = "abc" → [0,6]
  1. 知识点部分(Go 滑动窗口通用模板 & 数组 vs map) Go 滑动窗口模板 & 计数器选择

    • 固定窗口模板(这题典型):先初始化窗口 → 判断 → 然后 while 右指针移动,更新,判断,必要时移动左指针。

    • 为什么用 [26]int 而不是 map[string]int?

      • 数组访问 O(1),map 有 hash 开销 + 内存更大。
      • 小写字母限定时,数组碾压 map(性能 + 代码简洁)。
    • 面试常问:什么时候用 map?(字符集不确定或 unicode 时)。

    • 扩展:可变窗口(Day 1 的无重复子串),用 map + left 指针移动。

  2. 今日感悟 这道题是滑动窗口的经典应用,以前只刷过无重复子串那种可变窗口,今天固定窗口 + 数组计数器写出来才发现更高效。公开打卡让我必须把边界和优化点都搞清楚,不然发出去自己都心虚。Day 3 了,越来越觉得这个习惯在改变我。继续!

  3. 结束语 明天见! 欢迎评论区交流:① 你的滑动窗口模板怎么写 ② 代码哪里还能优化 ③ 你最近刷到的好题推荐 #程序员打卡 #LeetCode #Go语言 #滑动窗口 #字母异位词