🚩传送门:力扣题目

题目

给定两个字符串 [LC]438. 找到字符串中所有字母异位词 - 图1[LC]438. 找到字符串中所有字母异位词 - 图2,找到 [LC]438. 找到字符串中所有字母异位词 - 图3 中所有 [LC]438. 找到字符串中所有字母异位词 - 图4异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

示例 1:

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

解题思路:滑动窗口

我们可以在字符串 [LC]438. 找到字符串中所有字母异位词 - 图5 中构造一个长度为与字符串 [LC]438. 找到字符串中所有字母异位词 - 图6 的长度相同的滑动窗口,并在滑动中维护窗口中每种字母的数量;
当窗口中每种字母的数量与字符串 [LC]438. 找到字符串中所有字母异位词 - 图7 中每种字母的 数量相同 时,则说明当前窗口为字符串 [LC]438. 找到字符串中所有字母异位词 - 图8 的异位词。

在算法的实现中,我们可以使用[LC]438. 找到字符串中所有字母异位词 - 图9来存储字符串 [LC]438. 找到字符串中所有字母异位词 - 图10 和滑动窗口中每种字母的数量。

复杂度分析

时间复杂度:[LC]438. 找到字符串中所有字母异位词 - 图11,其中 [LC]438. 找到字符串中所有字母异位词 - 图12 为字符串[LC]438. 找到字符串中所有字母异位词 - 图13的长度。

空间复杂度:[LC]438. 找到字符串中所有字母异位词 - 图14,其中 [LC]438. 找到字符串中所有字母异位词 - 图15 为字符串[LC]438. 找到字符串中所有字母异位词 - 图16的长度。

官方代码

  1. class Solution {
  2. public static List<Integer> findAnagrams(String s, String p) {
  3. if (s == null || p == null || s.length() < p.length())
  4. return new LinkedList<Integer>();
  5. LinkedList<Integer> res = new LinkedList<>();
  6. HashMap<Character, Integer> map = new HashMap<>(); // 窗口中字母个数
  7. HashMap<Character, Integer> pmap = new HashMap<>(); // 模式串字母个数
  8. // p长度,滑动窗口大小
  9. int k = p.length();
  10. // 预先 K 窗口,
  11. for (int i = 0; i < k; i++) {
  12. map.put(s.charAt(i), map.getOrDefault(s.charAt(i), 0) + 1);
  13. pmap.put(p.charAt(i), pmap.getOrDefault(p.charAt(i), 0) + 1);
  14. }
  15. // 开始滑动
  16. for (int i = p.length(); i < s.length(); i++) {
  17. // 字母数相同说明匹配
  18. if (map.equals(pmap)) {
  19. res.add(i - k);
  20. }
  21. // 添加i值
  22. map.put(s.charAt(i), map.getOrDefault(s.charAt(i), 0) + 1);
  23. // 删除i-k值,先个数减1,若为0则删除
  24. map.put(s.charAt(i - k), map.getOrDefault(s.charAt(i - k), 1) - 1);
  25. if (map.get(s.charAt(i - k)) == 0) map.remove(s.charAt(i - k));
  26. }
  27. if (map.equals(pmap)) {
  28. res.add(s.length() - k);
  29. }
  30. return res;
  31. }
  32. }