leetcode_438_Find All Anagrams in a String_哈希表_java實現


題目:

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".

 

解題思路:

這道題一個最重要的難點就是時間控制。另一個就是哈希表的應用。

筆者第一次是從s中逐步截取與p等長的子串m,然后再暴力遍歷分析m和p是否為Anagrams(只有字符順序不同的兩個字符串),總的時間復雜度為O(s*p*p)大概是O(n^3),自然不能通過。第二次時間優化是在比較m和p時,采用《如何判斷兩個String是否是Anagrams》一文中介紹的方法,總的時間復雜度是O(s*p),大概是O(n^2),然后依然沒有通過,超時。

最后一次進一步優化,使用滑動窗口,使得時間復雜度為為O(n),這就是算法的力量!

算法介紹:

(1)NumberOfDeference = p.length(),用來表示目前窗口中的字符和p的差異度(由於窗口最大時(為p.length())才有可能使NumberOfDeference為0,所以NumberOfDeference 為0時窗口中與p為Anagrams)。NumberOfDeference差異度的含義:一開始窗口左右指針都指向第一個,所以差異度最大為p.length();當窗口中進來一個p中有的字符,差異度就減一,出去一個有的差異度就增一;至於進來或出去的是p中沒有的則NumberOfDeference不變,反正窗口的最大值也不過是p.length()。

(2)窗口的左右兩個指針:left和right,分別指向窗口的左端和右端。

滑動窗口具體操作:

先滑動右指針,

1.1 加進來的字符如果該字符在數組asciiChars的計數中非負,則NumberOfDeference減一,否則不做操作,

1.2無論在不在數組asciiChars該字符相應位置計數都減一

1.3如果滑動完right,判斷如果窗口長度到達p.length()(如果長度到達p.length(),並且NumberOfDeference為0,則將此時的left值加入到result中),滑動左窗口。

滑動左指針:

2.1被踢出窗口的那個字符如果在數組asciiChars的計數中非負,則NumberOfDeference增一,不在則不做操作。

2.2無論在不在數組asciiChars該字符相應位置計數都加一

(3)上面1.1和2.1操作的原理:asciiChars中的記錄的數據是代表p中含有的的每個字符的數量去掉當前窗口中存在的p字符串中每個字符的數量,一開始窗口大小為0,啥都不存在,自然asciiChars中記錄的就是全部p中含有的的每個字符的數量,如果窗口中有一個,則asciiChars相應的記錄中就少一個。如果窗口中多包含一個p中沒有的(或者包含的某一個字符的數量比p中有的還多),那么這時候,asciiChars中相應的記錄值就會變成負數,代表當前窗口中包含相應字符的“多余”的數量。

      所以當進來一個的時候,如果相應記錄為非負,那么這個進入是有意義的,則差異度(NumberOfDeference)減一;當出去一個的時候,如果相應記錄為非負,那么這個出去是有意義的,則差異度(NumberOfDeference)加一。

(4)asciiChars用到哈希表思想,用來記錄p中每一種字符分別有多少個。詳見《如何判斷兩個String是否是Anagrams》一文。

代碼:

public static List<Integer> findAnagrams(String s, String p) {
        List<Integer> result = new ArrayList<Integer>();
        int NumberOfDeference = p.length();  //差異度指數
        int left=0,right=0;  //窗口左右指針
        int[] asciiChars = new int[256];  
//記錄p中字符有哪些及其數量的數組
        for (int i = p.length() - 1; i>=0; --i)
          {     ++asciiChars[p.charAt(i)];      } //記錄完畢
        
        for(;right<s.length();right++){ //滑動右窗口
            asciiChars[s.charAt(right)]--;  //在該字符相應位置減一
            if(asciiChars[s.charAt(right)]>=0) NumberOfDeference--; 
//如果加進來的那個在p中,NumberOfDeference減一
            if(right-left == (p.length()-1)){
 //如果這時窗口大小為p.length()
                if(NumberOfDeference==0) result.add(left);  
//這時出現一次匹配,將左窗口加到result中
                //下面是滑動左窗口的操作
                if(asciiChars[s.charAt(left)]>=0) {
                    NumberOfDeference++; 
//如果被踢出的那個在p中,NumberOfDeference加一
                }
                asciiChars[s.charAt(left)]++;
//數組中相應字符計數位置加回來
                left++; //左窗口向右滑動
            }
        }
        return result;
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM