{x}
blog image

Count the Number of Consistent Strings

You are given a string allowed consisting of distinct characters and an array of strings words. A string is consistent if all characters in the string appear in the string allowed.

Return the number of consistent strings in the array words.

 

Example 1:

Input: allowed = "ab", words = ["ad","bd","aaab","baa","badab"]
Output: 2
Explanation: Strings "aaab" and "baa" are consistent since they only contain characters 'a' and 'b'.

Example 2:

Input: allowed = "abc", words = ["a","b","c","ab","ac","bc","abc"]
Output: 7
Explanation: All strings are consistent.

Example 3:

Input: allowed = "cad", words = ["cc","acd","b","ba","bac","bad","ac","d"]
Output: 4
Explanation: Strings "cc", "acd", "ac", and "d" are consistent.

 

Constraints:

  • 1 <= words.length <= 104
  • 1 <= allowed.length <= 26
  • 1 <= words[i].length <= 10
  • The characters in allowed are distinct.
  • words[i] and allowed contain only lowercase English letters.

Solution Explanation and Code for 1684. Count the Number of Consistent Strings

This problem asks us to count the number of strings in a given array words that are "consistent" with a given string allowed. A string is consistent if all its characters are present in the allowed string.

We can solve this problem using two main approaches:

1. Hash Table (or Set) Approach:

This is a more straightforward approach. We first create a set (or hash table) containing all the characters in the allowed string. This allows for O(1) lookup time to check if a character exists in allowed. Then, we iterate through each string in words. For each string, we iterate through its characters, checking if each character is present in our set. If all characters are present, we increment a counter.

Time Complexity: O(m), where m is the total number of characters across all strings in words. This is because we iterate through the allowed string once to build the set, and then we iterate through all the characters in the words array.

Space Complexity: O(k), where k is the number of unique characters in allowed. This is the space used to store the set. In the worst case, k could be 26 (all lowercase English letters).

Code Implementation (Python):

class Solution:
    def countConsistentStrings(self, allowed: str, words: List[str]) -> int:
        allowed_set = set(allowed)
        count = 0
        for word in words:
            is_consistent = True
            for char in word:
                if char not in allowed_set:
                    is_consistent = False
                    break
            if is_consistent:
                count += 1
        return count
 

2. Bit Manipulation Approach:

This approach is more concise and potentially faster for large inputs. We represent the characters in allowed and each word as bitmasks. Each bit in the bitmask represents a letter of the alphabet (a=0, b=1, ... z=25). A bit is set to 1 if the corresponding letter is present in the string, and 0 otherwise.

We create a bitmask for allowed. Then, for each word in words, we create its bitmask. A word is consistent if its bitmask is a subset of the allowed bitmask (meaning all its set bits are also set in the allowed bitmask). We can check this using a bitwise AND operation. If word_mask & allowed_mask == word_mask, the word is consistent.

Time Complexity: O(m), where m is the total number of characters in words. We iterate through each character once to build the bitmask.

Space Complexity: O(1). We use a constant amount of extra space to store the bitmasks.

Code Implementation (Python):

class Solution:
    def countConsistentStrings(self, allowed: str, words: List[str]) -> int:
        allowed_mask = 0
        for char in allowed:
            allowed_mask |= (1 << (ord(char) - ord('a')))
 
        count = 0
        for word in words:
            word_mask = 0
            for char in word:
                word_mask |= (1 << (ord(char) - ord('a')))
            if (word_mask & allowed_mask) == word_mask:
                count += 1
        return count

Both approaches have the same time complexity, but the bit manipulation approach might offer a slight performance advantage due to its efficient bitwise operations. The hash table approach is generally easier to understand and implement. The choice between the two depends on your priorities (readability vs. potential performance gain). The provided solutions earlier contain implementations in various languages reflecting both approaches.