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

题目描述

给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。

字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。

说明:

  • 字母异位词指字母相同,但排列不同的字符串。
  • 不考虑答案输出的顺序。

示例 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” 的字母异位词。

思路分析

滑动窗口算法。

  1. 左右指针,初始化都为0,把[left,right]当成一个窗口;
  2. 不断增大right,直到窗口内的子串符合要求;
  3. 不断增大left,缩小窗口直到窗口内子串不符合要求,在缩小left时记录结果;
  4. 直到right到达字符串串尾。

对于如何判断是否符合要求,建立两个哈希表,一个needs记录pattern子串中字符出现次数,一个windows记录当前窗口子串中字符出现次数。用一个match记录符合规则的字符数。

当match等于needs的长度时,表明满足条件,开始缩小窗口。每次缩小时,都要再次判断条件,更新match。

注:Integer范围是-128-127,在范围内用缓存值,超过这个范围会new一个对象,所以得用equals()比较地址

代码实现

    public static List<Integer> findAnagrams(String s, String p) {
        List<Integer> list = new ArrayList<>();
        if (s.length() == 0 || p.length() == 0) {
            return list;
        }
        int left = 0, right = 0, match = 0;
        Map<Character, Integer> windows = new HashMap<>();
        Map<Character, Integer> needs = new HashMap<>();
        //子串可能出现多次
        for (char c : p.toCharArray()) {
            Integer times = needs.getOrDefault(c, 0);
            needs.put(c, times + 1);
        }
        char[] chars = s.toCharArray();

        while (right < chars.length) {
            char c1 = chars[right];
            //模式串有这个字符
            if (needs.containsKey(c1)) {
                Integer times = windows.getOrDefault(c1, 0);
                windows.put(c1, times + 1);
                //一个字符匹配完成
                if (needs.get(c1).equals(windows.get(c1))) {
                    match++;
                }
            }
            right++;
            //窗口已经符合要求
            while (match == needs.size()) {
                if (right - left == p.length()) {
                    //更新list,存left下标
                    list.add(left);
                }
                //存完就向右移动left
                //先检查left位置上的字符是不是要的字符
                char c2 = chars[left];
                if (needs.containsKey(c2)) {
                    Integer tmp = windows.get(c2);
                    tmp--;
                    //如果c2字符出现的次数少于模式串中出现的次数
                    if (tmp < needs.get(c2)) {
                        match--;
                    }
                    //tmp为0就删掉字符
                    if (tmp == 0) {
                        windows.remove(c2);
                    } else {
                        windows.put(c2, tmp);
                    }
                }
                left++;
            }
        }
        return list;
    }

发布了158 篇原创文章 · 获赞 12 · 访问量 5549

猜你喜欢

转载自blog.csdn.net/qq_34761012/article/details/104715138
今日推荐