438. Find All Anagrams in a String

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

My Solutions:

anagram 的定义是两个单词里出现的字母相同。

方法1:naive,把string里每一个char的len = p的substring都检查一遍

Time: O(n), n = length of s

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> res = new ArrayList<>();
        if (s == null || p == null || s.length() == 0 || p.length() == 0) return res;
        
        int len = p.length();
        for (int i = 0; i <= s.length() - len; i++) {
            if (isAna(s.substring(i, i + len), p)) {
                res.add(i);
            }
        }
        
        return res;
    }
    
    private boolean isAna(String s, String p) {
        int[] hash = new int[26];
        for (int i = 0; i < p.length(); i++) {
            hash[p.charAt(i) - 'a']++;
            hash[s.charAt(i) - 'a']--;
        }
        for (int c : hash) {
            if (c != 0) return false;
        }
        return true;
    }
}

方法2:

  • 先创建一个256大小的int array,把p里所有字符出现的频率记录下来。用counter记录p的长度

  • 然后用两个指针,left代表sliding window的左边,即单词的开始index,right向右移动创造sliding window。只要right没有到搜索s的最末端,就不停向右移动。

    • 在移动的过程中,right所在的字符如果在p里,就把counter 减1。

    • 检测counter是否为0。如果为0,说明p里的所有字符都已经找到,把left此时的数字加入结果。

    • 无论如何,right所在的字符在int array里的频率减1。然后移动right,right++。

    • 如果此时left和right之间的距离等于p的长度,说明window已经是p的长度。我们需要把left左移,让window继续前进。

      • 首先看此时int array里left所在的数字。如果此数字大于等于0,说明l所在的这个字母在p里出现过,因为没有出现过的字母会是负数。之前counter在这个情况下减去过1,所以此时count++把这个1加回来。

      • 无论如何要把int array里的left所在的字符频率也+1。left再右移,left++。

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        
        List<Integer> res = new ArrayList<>();
        if (s == null || p == null || s.length() == 0 || p.length() ==0) return res;
        
        // 记录p里letter出现的频率
        int[] hash = new int[256];
        for (char c : p.toCharArray()) hash[c]++;
        
        //counter is a number to record whether we have a anagram which has equal length as p
        int left = 0, right = 0, counter = p.length(); 
        
        while (right < s.length()) {
            
            // there is p's char in s,把counter减少1
            char curr = s.charAt(right);
            if (hash[curr] >= 1) counter--;
            
            // 如果counter==0,说明每个在p里的字母都在s里找到了
            if (counter == 0) res.add(left);
                
            // hash table decreases the right char by 1    
            hash[curr]--;    
            right++; //move the sliding window to one step right
            
            //if the window's size equals p, then we have to move left (narrow the window) to find the new match window
            if (right - left == p.length()) { 
                // only increase the count if the character is in p,因为之前只有这样的条件才会count--      
                if (hash[s.charAt(left)] >= 0) counter++;
                // 因为之前减少过hash[curr]--, 马上要移动left坐标,所以这里需要加1回来
                hash[s.charAt(left)]++; 
                left++;
            }
        }
        
        return res;
    }
}

Last updated