Link

Medium Hash Table Two Pointers

Amazon

Facebook

Google

Microsoft

Oracle

Uber

2020-05-17

438. Find All Anagrams in a String

Similar Questions: LeetCode Question 567

Question:

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

Solution:

Using hashmap to store the character appears in the p string. The integer or value means how many times of certain character left to use.

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

      List<Integer> result = new ArrayList<Integer>();
      if (s.length() < p.length()) return result;
      HashMap<Character, Integer> map = new HashMap<Character, Integer>();
      
      char[] sarr = s.toCharArray();
      
      for (char c: p.toCharArray()) {
          map.put(c, map.getOrDefault(c, 0) + 1);
      }
      
      for (int i = 0; i < s.length(); i++) {
          char c = sarr[i];
          map.put(c, map.getOrDefault(c, 0) - 1);
          
          // Have found all c, remove it from the map
          if (map.get(c) == 0) {
              map.remove(c);
          }
          
          if (i >= p.length()) {
              char c_remove = sarr[i - p.length()];
              map.put(c_remove, map.getOrDefault(c_remove, 0) + 1);
              if (map.get(c_remove) == 0) {
                  map.remove(c_remove);
              }
          }
          
          // Have found all character
          if (map.size() == 0) {
              result.add(i - p.length() + 1);
          }
      }
      
      return result;
    }
}

The following two solutions use the same idea of two pointers which run faster than hash map.

// Borrow from https://leetcode.com/problems/find-all-anagrams-in-a-string/discuss/92015/ShortestConcise-JAVA-O(n)-Sliding-Window-Solution
class Solution {
   public List<Integer> findAnagrams(String s, String p) {
      List<Integer> list = new ArrayList<>();
      
      if (s == null || s.length() == 0 || p == null || p.length() == 0) return list;
      
      int[] hash = new int[256]; 

      for (char c : p.toCharArray()) {
          hash[c]++;
      }
      
      int left = 0, right = 0, count = p.length();
      while (right < s.length()) {
          //move right everytime, if the character exists in p's hash, decrease the count
          //current hash value >= 1 means the character is existing in p
          if (hash[s.charAt(right++)]-- >= 1) count--; 
          
          //when the count is down to 0, means we found the right anagram
          //then add window's left to result list
          if (count == 0) list.add(left);

          //if we find the window's size equals to p, then we have to move left (narrow the window) to find the new match window
          //++ to reset the hash because we kicked out the left
          //only increase the count if the character is in p
          //the count >= 0 indicate it was original in the hash, cuz it won't go below 0
          if (right - left == p.length() && hash[s.charAt(left++)]++ >= 0) count++;
      }
      return list;
    }
}
class Solution {
   public List<Integer> findAnagrams(String s, String p) {
      List<Integer> list = new ArrayList<Integer>();
      if (s == null || s.length() == 0 || p == null || p.length() == 0 || s.length() < p.length()) return list;
      int[] hash = new int[256]; 
      for (char c : p.toCharArray()) {
          hash[c]++;
      }
      int left = 0, right = 0, count = p.length();
      while (right < s.length()) {
          
          hash[s.charAt(right)]--;
          
          if (hash[s.charAt(right)] >= 0) {
              count--; 
          } 
          
          if (count == 0) {
              list.add(left);
          } 
          
          if (right - left == p.length() - 1) {
              if (hash[s.charAt(left)] >= 0) {
                  count++;
              }
              hash[s.charAt(left)]++;
              left++;
          } 
          
          right++;
         
      }
      return list;
  }
}