1. 程式人生 > 其它 >【LeetCode刷題筆記(七十)】之438. 找到字串中所有字母異位詞

【LeetCode刷題筆記(七十)】之438. 找到字串中所有字母異位詞

技術標籤:# LeetCode指標字串leetcode資料結構

本文章由公號【開發小鴿】釋出!歡迎關注!!!


老規矩–妹妹鎮樓:

一. 題目

(一) 題幹

給定一個字串 s 和一個非空字串 p,找到 s 中所有是 p 的字母異位詞的子串,返回這些子串的起始索引。

字串只包含小寫英文字母,並且字串 s 和 p 的長度都不超過 20100。

說明:

字母異位詞指字母相同,但排列不同的字串。
不考慮答案輸出的順序。

(二) 示例

輸入:
s: "cbaebabacd" p: "abc"

輸出:
[0, 6]

解釋:
起始索引等於 0
的子串是 "cba", 它是 "abc" 的字母異位詞。 起始索引等於 6 的子串是 "bac", 它是 "abc" 的字母異位詞。

二. 題解

(一) 思路

1. 暴力解法

字母異位詞,即兩個字串中的字母都是相同的,就是順序不同。因此,我們可以將p串中的每個字元進行hash計數,存入一個hashmap中。然後遍歷字串s,對每個p.length()長度的s子串,判斷哪些子串是p的字母異位詞。 對每個s的子串中的每個字元,判斷是否出現在了p串中,如果出現了,則將hashmap中的對應字元出現次數相應地減掉1,如果某個字元沒有出現或者多出現了1次,減掉1後hashmap中就會出現負數,說明該子串不是,break後繼續遍歷。這樣就可以找到所有的子串了。


2. 滑動視窗 + 雙指標

基本思路與暴力解法相同,首先都是要記錄p串中字元的出現次數,然後用雙指標作為滑動視窗的左右指標,初始化都指向0。建立一個臨時計數陣列tmp[],記錄滑動視窗內每個字元的出現次數,與p串中字元出現次數進行比較。右指標不斷右移,每移入一個字元,將該字元的次數+1,如果該字元對應的數字大於p串中該字元的數字,則說明滑動視窗中該字元多了1個,則讓左指標不斷右移,將右移過程中碰到的字元的計數都-1,直到碰到一個該字元。這樣,滑動視窗恢復正常,右指標可繼續右移。若視窗的大小恰好為p串的長度,且每個字元的數字都沒有超過p串的數字,說明滑動視窗內的字串與p串是異位字串,將視窗的左指標儲存。


(二) 程式碼實現

1. 暴力解法

Java:

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        //字串 操作
        int sn = s.length();
        int pn = p.length();
        ArrayList<Integer> list = new ArrayList<>();
        if(s == null || p == null || sn < pn){
            return new ArrayList<Integer>();
        }
        // 由於字母只有26個,用陣列儲存出現次數即可
        int[] map = new int[26];
        for(int i = 0; i < pn; ++i){
            map[p.charAt(i) - 'a']++;
        }
        //遍歷s,取子字串
        for(int i = 0 ; i <= sn-pn; ++i){
            int[] tmp = Arrays.copyOf(map, 26);
            int j = i;
            for(; j < i+pn; ++j){
                if(--tmp[s.charAt(j)-'a'] < 0){
                    break;
                }
            }
            // 找到
            if(j == i+ pn){
                list.add(i);
            }
        }
        return list;
    }
}

2. 滑動視窗+ 雙指標

Java:

// 滑動視窗 + 雙指標
        // //字串 操作
        int sn = s.length();
        int pn = p.length();
        ArrayList<Integer> list = new ArrayList<>();
        if(s == null || p == null || sn < pn){
            return new ArrayList<Integer>();
        }
        // 由於字母只有26個,用陣列儲存出現次數即可
        int[] map = new int[26];
        for(int i = 0; i < pn; ++i){
            map[p.charAt(i) - 'a']++;
        }
        //雙指標
        int left = 0, right = 0;
        // 滑動視窗陣列
        int[] win = new int[26];
        while(left < sn && right < sn){
            // 右指標為當前判斷的字元
            int cur = s.charAt(right) - 'a';
            win[cur]++;
            right++;
            // 當右指標所指的字元次數超了,就右移左指標
            while(win[cur] > map[cur]){
                win[s.charAt(left)-'a']--;
                left++;
            }
            //如果滑動視窗與pn相等,說明找到一個
            if(right - left == pn){
                list.add(left);
            }
        }
        return list;