1. 程式人生 > >[LeetCode] 438. Find All Anagrams in a String

[LeetCode] 438. Find All Anagrams in a String

題目描述

Given a string s and a non-empty string p, find all the start indices of p's anagrams in s.

Strings consists of lowercase English letters only and the length of both strings s and p will not be larger than 20,100.

The order of output does not matter.

Example 1:

Input:
s: "cbaebabacd" p: "abc"

Output:
[0, 6] Explanation: The substring with start index = 0 is "cba", which is an anagram of "abc". The substring with start index = 6 is "bac", which is an anagram of "abc".

Example 2:

Input:
s: "abab" p: "ab"

Output:
[0, 1, 2]

Explanation:
The substring with start index = 0 is "ab", which is an anagram of "ab".
The substring with start index = 1 is "ba", which is an anagram of "ab".
The substring with start index = 2 is "ab", which is an anagram of "ab".

 題目分析

本題要求求出只包含給定字串p的在字串s中的開始位置的陣列,陣列中每一位都是字串p在s中的起始位置

具體解法

在一個字串中求子字串的個數,這類題很容易想到使用滑動視窗, 對於字母的出現次數的統計,可以使用字典的方法進行記錄,這裡使用了一個數組來記錄每個字串出現的次數,剛開始先將p中的字元填入陣列中作為默認出現次數,在視窗進行移動的時候,右邊界每次找到一個元素,就將這個元素的出現次數在字典中-1,注意:只有在p中出現的字母在字典中的數量才會>=1,其餘字母的出現次數預設都是0,當左邊界經過一個數的時候,將這個數在字典中+1,也就是還原這個數的出現次數,當右邊界右移,發現當前數在字典中的出現次數>=1,說明找到了一個字典中的數,將count(p的長度)--,代表找到了一個p中的字母,在左邊界右移的時候,如果發現當前字母出現次數>=0,說明是p中的字母,將count++,證明這個字母已經被排除了,簡單來說就是右邊-字母出現次數,左邊界+字母出現次數

import java.util.ArrayList;
import java.util.List;

public class Solution {

    public List<Integer> findAnagrams(String s, String p) {

        if (s == null || p == null || s.length() == 0 || p.length() == 0) {

            return new ArrayList<>();
        }

        // 記錄每個字母出現的次數
        int[] built = new int[26];

        // 開始的時候記錄p中元素出現的次數
        for (char c : p.toCharArray()) {

            built[c - 'a']++;
        }

        int left = 0;
        int right = 0;
        // p中元素出現的次數,當為0的時候,代表所有的元素都出現了一次
        int count = p.length();
        List<Integer> res = new ArrayList<>();

        while (right < s.length()) {

            // 如果當前的這個元素在built中出現的次數>=1 說明這個元素是p中的,因為沒有出現過的元素都是0
            // built[s.charAt(right) - 'a']-- 代表當前這個元素在右邊界已經遍歷過了,將其減去,在左邊界經過的時候進行+1還原其出現次數
            if (built[s.charAt(right++) - 'a']-- >= 1) {
                count--;
            }

            // p中的元素完全出現了
            if (count == 0) {

                res.add(left);
            }

            // 如果視窗超過了p的長度,說明要把左指標進行右移,將上面將去的元素+1,例:left=1 right=4,視窗包含了4個元素 但s.length=3,所以要將左邊界右移
            // 如果遍歷的元素的個數是0(說明是p中的元素,其餘的元素 右邊界減過之後都是<0的)將count+1
            // built[s.charAt(right) - 'a']--;代表將右邊界減去的元素還原
            if (right - left == p.length() && built[s.charAt(left++) - 'a']++ >= 0) {
                count++;
            }
        }
        return res;
    }

    public static void main(String[] args) {

        Solution s = new Solution();
        System.out.println(s.findAnagrams("cbaebabacd", "abc"));
    }
}