LeetCode:438. 找到字符串中所有字母异位词

简介

题目链接:https://leetcode.cn/problems/find-all-anagrams-in-a-string/description/

解决方式:字符串 + 滑动窗口、哈希表 / 数组

这是作者学习众多大神的思路进行解题的步骤,很推荐大家解题的时候去看看题解里面大佬们的思路、想法!

滑动窗口

思路:这题的思路与76.最小覆盖子串题目类似,都是使用滑动窗口然后通过哈希表或数组第三方数据结构判断滑动窗口的扩大、收缩时机。不一样的是,收缩条件变为滑动窗口的长度等于目标的长度。在这种情况下,valid 变量等于所需字符种类数,相当于固定长度(长度等于目标)的滑动窗口包含了所有目标字符种类且满足数量,该窗口字串就是目标的排列或字母异味词了。

哈希表

推荐查看 labuladong 大佬的题解!

java 复制代码
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        // 哈希表,所需的字符种类和数量
        HashMap<Character,Integer> need = new HashMap();
        for(int i = 0; i < p.length(); i++){
            char c = p.charAt(i);
            need.put(c, need.getOrDefault(c, 0) + 1);
        }
        // 哈希表,滑动窗口中对于的哈希值
        HashMap<Character,Integer> window = new HashMap();
        // 双指针
        int left = 0;
        int right = 0;
        // 滑动窗口中满足目标字符的个数
        int valid = 0;
        // 结果
        ArrayList<Integer> list = new ArrayList();
        // 迭代
        while(right < s.length()){
            // 当前字符
            char c = s.charAt(right);
            // 滑动窗口扩大
            right++;
            // 判断
            if(need.containsKey(c)){
                // 当前元素是目标字符,加入滑动窗口对于的字符哈希表中
                window.put(c, window.getOrDefault(c, 0) + 1);
                if(window.get(c).equals(need.get(c))){
                    // 滑动窗口中的该字符满足目标
                    valid++;
                }
            }
            // 滑动窗口保持固定大小移动(缩小)
            while(right - left == p.length()){
                if(valid == need.size()){
                    // 大小一致,目标字符种类数量也满足,当前子串就是异位词
                    list.add(left);
                }
                // 左边界移动
                char cleft = s.charAt(left);
                left++;
                // 元素为目标字符
                if(need.containsKey(cleft)){
                    if(window.get(cleft).equals(need.get(cleft))){
                        valid--;
                    }
                    window.put(cleft, window.getOrDefault(cleft, 0) - 1);
                }
            }
        }
        // 返回结果
        return list;
    }
}

数组

推荐查看 灵茶山艾府 大佬的题解!

java 复制代码
class Solution {
    // 滑动窗口
    // 大体思路是有两个数组,一个代表滑动窗口,一个代表目标字符串,滑动窗口的大小于与目标字符串相同
    // 移动滑动窗口,比较两数组是否相同。相同则表示寻找到目标的字母异位体,因为两者的字符、数量相同
    public List<Integer> findAnagrams(String s, String p) {
        int sLen = s.length(), pLen = p.length();
        // 特例判断
        if(sLen < pLen){
            return new ArrayList<Integer> ();
        }
        // 初始化数组和结果集
        ArrayList<Integer> ans = new ArrayList<>();
        int[] sCount = new int[26];
        int[] pCount = new int[26];
        // 初始化滑动窗口
        for(int i = 0; i < pLen; i++){
            // 字符与数组索引映射
            ++sCount[s.charAt(i) - 'a'];
            ++pCount[p.charAt(i) - 'a'];
        }
        if(Arrays.equals(sCount, pCount)){
            ans.add(0);
        }
        // 移动滑动窗口
        // 此时,i 代表滑动窗口的左边界以及 s 中剩下的字符,即滑动窗口需要移动的次数
        for(int i = 0; i < sLen - pLen; i++){
            // 去除左边界元素
            --sCount[s.charAt(i) - 'a'];
            // 滑动窗口向右移动
            ++sCount[s.charAt(i + pLen) - 'a'];
            // 判断
            if(Arrays.equals(sCount, pCount)){
                // 初始位置为左边界 + 1
                ans.add(i + 1);
            }
        }
        return ans;
    }
}

数组优化

java 复制代码
class Solution {
    // 滑动窗口优化
    // 大体思路是维护一个数组和一个不同变量 differ,取代两个数组的全量比较
    public List<Integer> findAnagrams(String s, String p) {
        int sLen = s.length(), pLen = p.length();
        // 特例判断
        if(sLen < pLen){
            return new ArrayList<Integer> ();
        }
        // 初始化数组和结果集
        ArrayList<Integer> ans = new ArrayList<>();
        int[] count = new int[26];
        // 初始化滑动窗口
        for(int i = 0; i < pLen; i++){
            // 字符与数组索引映射
            ++count[s.charAt(i) - 'a'];
            --count[p.charAt(i) - 'a'];
        }
        // 记录当前窗口与目标字符种类不同的个数,检验是否找到字母异位体
        // 相同字符不同个数不重复计入
        int differ = 0;
        for (int j = 0; j < 26; ++j) {
            if (count[j] != 0) {
                ++differ;
            }
        }
        if(differ == 0){
            ans.add(0);
        }
        // 移动滑动窗口
        // 此时,i 代表滑动窗口的左边界以及 s 中剩下的字符,即滑动窗口需要移动的次数
        // 检验是否异位体,是通过判断 differ 是否为 0
        for(int i = 0; i < sLen - pLen; i++){
            // 移动左边界
            // 左边界的变动会导致滑动窗口的字符发生变化
            // 需要判断会不会导致 differ 变化,即窗口找到异位体
            if (count[s.charAt(i) - 'a'] == 1) {
                --differ;
            } else if (count[s.charAt(i) - 'a'] == 0) {
                ++differ;
            }
            --count[s.charAt(i) - 'a']; // 真正移动
            // 移动右边界
            // 右边界的变动也会导致滑动窗口的字符发生变化
            // 也需要判断会不会导致 differ 变化,即窗口找到异位体
            if (count[s.charAt(i + pLen) - 'a'] == -1) {
                --differ;
            } else if (count[s.charAt(i + pLen) - 'a'] == 0) {
                ++differ;
            }
            ++count[s.charAt(i + pLen) - 'a']; // 真正移动
            // 判断
            if (differ == 0) {
                ans.add(i + 1);
            }
        }
        return ans;
    }
}
相关推荐
先吃饱再说15 小时前
判断回文字符串,从一行代码到双指针优化
算法
黄敬峰17 小时前
深入理解算法核心:从递归思想、数组扁平化到快速排序
算法
得物技术19 小时前
从狂野代码到按目标生产:得物推荐 AI Harness 的工程化实践|AICon 演讲整理
人工智能·算法·架构
AI小老六1 天前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
后端·算法·ai编程
胡萝卜术1 天前
从“分数打架”到“排名投票”:为什么你的ChatBI必须用RRF?
算法·设计模式·面试
Asize1 天前
初识DFS 与 BFS:递归、队列与图遍历
算法
罗西的思考2 天前
机器人 / 强化学习】HIL-SERL:人类在环驱动的具身智能进化框架
人工智能·算法·机器学习
美团技术团队2 天前
LongCat 开源 VitaBench 2.0:长期动态智能体基准新标杆
人工智能·算法
To_OC2 天前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode