3 minute read

Problem Statement

Given two strings s and p, return an array of all the start indices of p's anagrams in s. You may return the answer in any order.

An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.

 

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

Constraints:

1 <= s.length, p.length <= 3 * 10^4
s and p consist of lowercase English letters.

Intuition

My initial thoughts on solving this problem revolved around utilizing a sliding window approach to efficiently find anagrams in a given string.

Approach

My approach involved maintaining counters for both the pattern string (p) and the current window in the input string (s). I iterated through the string, updating the window’s counter and adjusting the window boundaries accordingly. When the counters matched, it indicated the presence of an anagram, and I recorded the starting index of the anagram.

Complexity

  • Time complexity: O(n), where n is the length of the input string s. This is because I iterate through the string once, and the operations within the loop take constant time.

  • Space complexity: O(1), as the size of the counter dictionaries is constant and independent of the input size. (only 26 characters in English)

Code

class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        res = []
        counter_p = Counter(p)
        counter = Counter()
        start = end = 0
        for end in range(len(s)):
            counter[s[end]] += 1
            
            while counter[s[end]] > counter_p[s[end]]:
                counter[s[start]] -= 1
                if counter[s[start]] <= 0:
                    del counter[s[start]]
                start += 1
            
            if counter == counter_p:
                res.append(start)
        
        return res

Editorial Solution

Approach 1: Sliding Window with HashMap

from collections import Counter
class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        ns, np = len(s), len(p)
        if ns < np:
            return []

        p_count = Counter(p)
        s_count = Counter()
        
        output = []

        # sliding window on the string s
        for i in range(ns):
            # Add one more letter 
            # on the right side of the window
            s_count[s[i]] += 1

            # Remove one letter 
            # from the left side of the window
            if i >= np:
                if s_count[s[i - np]] == 1:
                    del s_count[s[i - np]]
                else:
                    s_count[s[i - np]] -= 1

            # Compare array in the sliding window
            # with the reference array
            if p_count == s_count:
                output.append(i - np + 1)
        
        return output

Approach 2: Sliding Window with Array

class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        ns, np = len(s), len(p)
        if ns < np:
            return []

        p_count, s_count = [0] * 26, [0] * 26
        # build reference array using string p
        for ch in p:
            p_count[ord(ch) - ord('a')] += 1
        
        output = []
        # sliding window on the string s
        for i in range(ns):
            # add one more letter 
            # on the right side of the window
            s_count[ord(s[i]) - ord('a')] += 1
            # remove one letter 
            # from the left side of the window
            if i >= np:
                s_count[ord(s[i - np]) - ord('a')] -= 1
            # compare array in the sliding window
            # with the reference array
            if p_count == s_count:
                output.append(i - np + 1)
        
        return output