A string is consistent if every character in it appears in the allowed string. The straightforward approach is to check each word character by character. For each character, we scan through the allowed string to see if it exists. If any character is not found, the word is inconsistent.
res to 0.words:true (assuming the word is consistent).allowed by scanning through allowed.false and break out of the inner loop.true after checking all characters, increment res.res.Where is the number of words, is the length of the string , and is the length of the longest word.
The brute force approach is slow because we scan through allowed for every character lookup. We can speed this up by storing all allowed characters in a hash set, which provides O(1) average lookup time. Instead of counting consistent words, we can start with the total count and subtract whenever we find an inconsistent word.
allowed into a hash set for O(1) character lookups.res to the total number of words.words:res and break out of the inner loop.res.Where is the number of words, is the length of the string , and is the length of the longest word.
Since we are only dealing with lowercase English letters (26 characters), we can use a boolean array of size 26 instead of a hash set. Each index represents a letter ('a' = 0, 'b' = 1, ..., 'z' = 25). This provides the same O(1) lookup time as a hash set but with slightly better constant factors due to simpler memory access patterns.
false.allowed, mark the corresponding index as true.res to the total number of words.words:true.false, decrement res and break out of the inner loop.res.class Solution:
def countConsistentStrings(self, allowed: str, words: List[str]) -> int:
allowedArr = [False] * 26
for c in allowed:
allowedArr[ord(c) - ord('a')] = True
res = len(words)
for w in words:
for c in w:
if not allowedArr[ord(c) - ord('a')]:
res -= 1
break
return resWhere is the number of words, is the length of the string , and is the length of the longest word.
We can compress the boolean array into a single 32-bit integer using bit manipulation. Each bit position represents whether a character is allowed (bit i represents the character 'a' + i). This approach uses constant space (just one integer) and leverages fast bitwise operations for lookups.
0.allowed, set the corresponding bit using OR operation: bit_mask |= (1 << (char - 'a')).res to the total number of words.words:0 (bit not set), decrement res and break out of the inner loop.res.class Solution:
def countConsistentStrings(self, allowed: str, words: List[str]) -> int:
bit_mask = 0
for c in allowed:
bit = 1 << (ord(c) - ord('a'))
bit_mask = bit_mask | bit
res = len(words)
for w in words:
for c in w:
bit = 1 << (ord(c) - ord('a'))
if bit & bit_mask == 0:
res -= 1
break
return resWhere is the number of words, is the length of the string , and is the length of the longest word.
When a word is found to be inconsistent, failing to break out of the inner loop wastes time checking remaining characters. This does not affect correctness but can significantly impact performance.
# Incorrect - continues checking after finding invalid character
for c in w:
if c not in allowed:
flag = False
# Missing break!
# Correct - exits immediately when inconsistent
for c in w:
if c not in allowed:
flag = False
breakWhen using the bitmask approach, a common mistake is checking if the bit equals 1 instead of checking if it is non-zero. The bit position varies, so the result of the AND operation will be a power of 2, not necessarily 1.
# Incorrect - bit & mask could be 2, 4, 8, etc., not 1
if (bit & bit_mask) == 1:
res -= 1
# Correct - check if the result is zero
if (bit & bit_mask) == 0:
res -= 1