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;
    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章