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

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

题目详情

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

问题分析

字母异位词的关键特征是:

字符组成完全相同(字符种类及每个字符的出现次数相同)

字符顺序可以不同

因此,判断两个字符串是否为字母异位词,可以转化为判断它们字符频率分布是否相同。

核心思路:滑动窗口

由于我们要在字符串 s 中寻找与 p 长度相同的异位词子串,可以使用固定大小的滑动窗口来遍历 s,窗口大小即为 p 的长度。然后比较窗口内字符的频率分布与 p 的频率分布是否一致。

方法一:固定窗口比较法

最直接的思路是每次移动窗口后,都比较窗口内字符频率与 p 的字符频率。

java 复制代码
public List<Integer> findAnagrams(String s, String p) {
    List<Integer> result = new ArrayList<>();
    if (s.length() < p.length()) return result;
    
    int[] pCount = new int[26];
    int[] sCount = new int[26];
    
    // 统计p的字符频率,并初始化第一个窗口
    for (int i = 0; i < p.length(); i++) {
        pCount[p.charAt(i) - 'a']++;
        sCount[s.charAt(i) - 'a']++;
    }
    
    // 检查第一个窗口
    if (Arrays.equals(pCount, sCount)) {
        result.add(0);
    }
    
    // 滑动窗口:每次向右移动一位
    for (int i = 0; i < s.length() - p.length(); i++) {
        // 移除左边界字符
        sCount[s.charAt(i) - 'a']--;
        // 添加右边界字符
        sCount[s.charAt(i + p.length()) - 'a']++;
        
        // 检查当前窗口
        if (Arrays.equals(pCount, sCount)) {
            result.add(i + 1);
        }
    }
    
    return result;
}

复杂度分析:

时间复杂度:O(n × 26),其中 n 是字符串 s 的长度,每次比较数组需要 O(26) 时间

空间复杂度:O(26),使用固定大小的数组

方法二:计数差分法(优化版)

我们可以通过维护一个 count 变量来优化判断过程,避免每次都比较整个数组。

java 复制代码
public List<Integer> findAnagrams(String s, String p) {
    List<Integer> result = new ArrayList<>();
    if (s.length() < p.length()) return result;
    
    int[] count = new int[26];
    
    // 初始化p的字符统计
    for (char c : p.toCharArray()) {
        count[c - 'a']++;
    }
    
    int left = 0, right = 0;
    int needToMatch = p.length();
    
    while (right < s.length()) {
        // 右指针字符进入窗口
        char rightChar = s.charAt(right);
        if (count[rightChar - 'a'] > 0) {
            needToMatch--;
        }
        count[rightChar - 'a']--;
        right++;
        
        // 当窗口大小等于p长度时,检查结果
        if (right - left == p.length()) {
            if (needToMatch == 0) {
                result.add(left);
            }
            
            // 左指针字符移出窗口
            char leftChar = s.charAt(left);
            if (count[leftChar - 'a'] >= 0) {
                needToMatch++;
            }
            count[leftChar - 'a']++;
            left++;
        }
    }
    
    return result;
}

优化原理

count 数组记录当前窗口中字符与 p 中字符的差值

needToMatch 跟踪还需匹配的字符总数

当 needToMatch == 0 时,说明窗口内字符与 p 完全匹配

方法三:滑动窗口模板(通用解法)

这是一种更通用的滑动窗口解法,使用 valid 变量来跟踪已匹配的字符种类数。

java 复制代码
public List<Integer> findAnagrams(String s, String p) {
    List<Integer> result = new ArrayList<>();
    Map<Character, Integer> need = new HashMap<>();
    Map<Character, Integer> window = new HashMap<>();
    
    // 初始化need映射
    for (char c : p.toCharArray()) {
        need.put(c, need.getOrDefault(c, 0) + 1);
    }
    
    int left = 0, right = 0;
    int valid = 0;
    
    while (right < s.length()) {
        // 扩大右边界
        char rightChar = s.charAt(right);
        right++;
        
        if (need.containsKey(rightChar)) {
            window.put(rightChar, window.getOrDefault(rightChar, 0) + 1);
            if (window.get(rightChar).equals(need.get(rightChar))) {
                valid++;
            }
        }
        
        // 当窗口大小达到p长度时,收缩左边界
        while (right - left >= p.length()) {
            // 检查是否找到异位词
            if (valid == need.size()) {
                result.add(left);
            }
            
            char leftChar = s.charAt(left);
            left++;
            
            if (need.containsKey(leftChar)) {
                if (window.get(leftChar).equals(need.get(leftChar))) {
                    valid--;
                }
                window.put(leftChar, window.get(leftChar) - 1);
            }
        }
    }
    
    return result;
}

关键点与技巧

窗口大小固定:窗口大小始终为 p.length(),每次移动只需考虑进出的两个字符

字符频率统计:使用长度为26的数组统计小写字母频率,比HashMap更高效

优化判断:通过 count 或 valid 变量避免全数组比较,提升效率

边界处理:当 s.length() < p.length() 时直接返回空列表

性能对比

总结

滑动窗口是解决子串搜索问题的利器。对于字母异位词问题,关键是抓住字符频率一致性这一特征,通过维护窗口内字符频率来高效判断。

对于此题,推荐使用计数差分法,它在实现复杂度和性能之间取得了良好平衡。理解并掌握这种滑动窗口的优化技巧,对解决类似的字符串搜索问题大有裨益。

相关推荐
毕设小屋vx ylw2824264 小时前
Java开发、Java Web应用、前端技术及Vue项目
java·前端·vue.js
TDengine (老段)5 小时前
TDengine 字符串函数 CHAR 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
float_com5 小时前
【java基础语法】------ 数组
java
Adellle5 小时前
2.单例模式
java·开发语言·单例模式
零雲5 小时前
java面试:有了解过RocketMq架构么?详细讲解一下
java·面试·java-rocketmq
Deamon Tree5 小时前
HBase 核心架构和增删改查
java·hbase
gihigo19985 小时前
MATLAB使用遗传算法解决车间资源分配动态调度问题
算法·matlab
墨染点香5 小时前
LeetCode 刷题【138. 随机链表的复制】
算法·leetcode·链表