PU Find All Anagrams in a String

Jan 01, 1970

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

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

C Solution:

/**
 * Return an array of size *returnSize.
 * Note: The returned array must be malloced, assume caller calls free().
 */
void save(int **res, int *size, int *returnSize, int val) {
    if (*size == *returnSize) {
        *size += 1000;
        *res = realloc(*res, *size * sizeof(int));
    }
    (*res)[(*returnSize)++] = val;
}
int* findAnagrams(char* s, char* p, int* returnSize) {
    if (strlen(s) < strlen(p)) return 0;
    int size = 1000;
    int *res = malloc(size * sizeof(int));
    *returnSize = 0;

    int i, diff = 0, flag[256] = {0};
    for (i = 0; p[i]; i++) {
        if (s[i] == p[i]) continue;
        diff += ++flag[s[i]] > 0 ? 1 : -1;
        diff += --flag[p[i]] < 0 ? 1 : -1;
    }
    if (!diff) save(&res, &size, returnSize, 0);
    int j;
    for (j = i; s[j]; j++) {
        diff += ++flag[s[j]] > 0 ? 1 : -1;
        diff += --flag[s[j - i]] < 0 ? 1 : -1;
        if (!diff) save(&res, &size, returnSize, j - i + 1);
    }

    return res;
}

Python Solution 1:

class Solution(object):
    def findAnagrams(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: List[int]
        """
        if len(s) < len(p): return []
        d = collections.Counter(p)
        diff = len(p)
        res = []
        for i in range(len(p)):
            if d[s[i]] > 0:
                diff -= 1
            else:
                diff += 1
            d[s[i]] -= 1
        if not diff: res.append(0)
        for i in range(len(p), len(s)):
            if d[s[i]] > 0:
                diff -= 1
            else:
                diff += 1
            d[s[i]] -= 1
            if d[s[i - len(p)]] < 0:
                diff -= 1
            else:
                diff += 1
            d[s[i - len(p)]] += 1
            if not diff: res.append(i - len(p) + 1)
        return res

Python Solution 2:

class Solution(object):
    def findAnagrams(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: List[int]
        """
        if not s or not p or len(s) < len(p): return []
        d = collections.Counter(p)
        diff = len(p)
        res = []
        for i in range(len(p)):
            c = s[i]
            diff += 1 if d[c] <= 0 else -1
            d[c] -= 1
        if not diff: res.append(0)
        for i in range(len(p), len(s)):
            c = s[i]
            diff += 1 if d[c] <= 0 else -1
            d[c] -= 1
            c = s[i - len(p)]
            diff += 1 if d[c] >= 0 else -1
            d[c] += 1
            if not diff: res.append(i - len(p) + 1)
        return res

Python Solution 3:

class Solution(object):
    def findAnagrams(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: List[int]
        """
        if not s or not p or len(s) < len(p): return []
        res = []
        pp = collections.Counter(p)
        ss = collections.Counter(s[:len(p)])
        if ss == pp: res.append(0)
        for i in range(len(p), len(s)):
            c = s[i]
            ss[c] += 1
            c = s[i - len(p)]
            if ss[c] == 1: del ss[c]
            else: ss[c] -= 1
            if ss == pp: res.append(i - len(p) + 1)
        return res

Summary:

  1. 16ms, 76.74%
  2. Maybe anagram problems can all use this idea: use a diff, one direction for add, the other for del.
  3. Similar to 242. Valid Anagram

LeetCode: 438. Find All Anagrams in a String