2255. Count Prefixes of a Given String
Problem Description
You are given an array of strings words
and a string s
. All strings contain only lowercase English letters.
Your task is to count how many strings from the words
array are prefixes of the string s
.
A prefix is a substring that appears at the beginning of a string. For example:
"ab"
is a prefix of"abc"
"abc"
is a prefix of"abcdef"
"bc"
is NOT a prefix of"abcd"
(it doesn't start from the beginning)
The solution uses Python's built-in startswith()
method to check if s
starts with each word w
from the array. The expression s.startswith(w)
returns True
if w
is a prefix of s
, and False
otherwise. By using sum()
on these boolean values (where True
counts as 1 and False
as 0), we get the total count of words that are prefixes of s
.
For example, if words = ["a", "abc", "bc", "d"]
and s = "abc"
, the answer would be 2 because:
"a"
is a prefix of"abc"
✓"abc"
is a prefix of"abc"
✓"bc"
is NOT a prefix of"abc"
✗"d"
is NOT a prefix of"abc"
✗
Intuition
The problem asks us to find which strings from an array are prefixes of a given string. The most straightforward approach is to check each word individually against the target string.
When we think about checking if one string is a prefix of another, we need to verify that the target string starts with exactly the same characters as the potential prefix. For instance, if we want to check if "ab"
is a prefix of "abc"
, we need to confirm that the first two characters of "abc"
match "ab"
exactly.
Python provides a built-in method startswith()
that does exactly this check - it returns True
if a string begins with the specified prefix, and False
otherwise. This eliminates the need to manually compare characters or slice strings.
Since we need to count how many words satisfy this condition, we can leverage Python's ability to treat boolean values as numbers (True
as 1, False
as 0). By using a generator expression s.startswith(w) for w in words
, we create a sequence of boolean values indicating whether each word is a prefix. The sum()
function then adds these up, effectively counting the True
values.
This approach is both concise and efficient - we make a single pass through the array, perform one prefix check per word, and directly obtain our count. The time complexity is O(n * m)
where n
is the number of words and m
is the average length of the words (for the prefix comparison), which is optimal since we must examine each word at least once.
Solution Approach
The solution implements a traversal counting approach where we iterate through each word in the words
array and check if it's a prefix of the string s
.
The implementation uses a single line of code that combines iteration and counting:
return sum(s.startswith(w) for w in words)
Let's break down how this works:
-
Iteration through the array: We use a generator expression
for w in words
to iterate through each string in thewords
array. -
Prefix checking: For each word
w
, we calls.startswith(w)
. This method checks if the strings
begins with the substringw
. It returns:True
ifw
is a prefix ofs
False
ifw
is not a prefix ofs
-
Counting mechanism: The
sum()
function adds up all the boolean values from the generator expression. In Python,True
is treated as 1 andFalse
as 0 when used in arithmetic operations. Therefore,sum()
effectively counts the number ofTrue
values, which represents the number of words that are prefixes ofs
.
For example, if we have:
words = ["a", "ab", "bc"]
s = "abc"
The execution would be:
s.startswith("a")
→True
(1)s.startswith("ab")
→True
(1)s.startswith("bc")
→False
(0)sum([True, True, False])
→sum([1, 1, 0])
→ 2
The algorithm makes a single pass through the array with no additional data structures needed, making it both space-efficient (O(1)
extra space) and straightforward to implement.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to illustrate how the solution works.
Given:
words = ["ab", "a", "abc", "bc", "d"]
s = "abcd"
Goal: Count how many strings from words
are prefixes of "abcd"
.
Step-by-step execution:
-
Check "ab":
- Does "abcd" start with "ab"?
- Compare: "ab" matches the first 2 characters of "abcd"
s.startswith("ab")
→True
(counts as 1)
-
Check "a":
- Does "abcd" start with "a"?
- Compare: "a" matches the first character of "abcd"
s.startswith("a")
→True
(counts as 1)
-
Check "abc":
- Does "abcd" start with "abc"?
- Compare: "abc" matches the first 3 characters of "abcd"
s.startswith("abc")
→True
(counts as 1)
-
Check "bc":
- Does "abcd" start with "bc"?
- Compare: "abcd" starts with "a", not "b"
s.startswith("bc")
→False
(counts as 0)
-
Check "d":
- Does "abcd" start with "d"?
- Compare: "abcd" starts with "a", not "d"
s.startswith("d")
→False
(counts as 0)
Final calculation:
- The generator expression produces:
[True, True, True, False, False]
- When passed to
sum()
:[1, 1, 1, 0, 0]
- Result:
1 + 1 + 1 + 0 + 0 = 3
Therefore, 3 strings from the array ("ab", "a", and "abc") are prefixes of "abcd".
Solution Implementation
1class Solution:
2 def countPrefixes(self, words: List[str], s: str) -> int:
3 """
4 Count the number of strings in words that are prefixes of string s.
5
6 Args:
7 words: List of strings to check as potential prefixes
8 s: Target string to check prefixes against
9
10 Returns:
11 Integer count of how many words are prefixes of s
12 """
13 # Initialize counter for prefix matches
14 count = 0
15
16 # Iterate through each word in the list
17 for word in words:
18 # Check if current word is a prefix of string s
19 if s.startswith(word):
20 count += 1
21
22 return count
23```
24
25Alternative implementation using the original concise approach with comments:
26
27```python3
28class Solution:
29 def countPrefixes(self, words: List[str], s: str) -> int:
30 """
31 Count the number of strings in words that are prefixes of string s.
32
33 Args:
34 words: List of strings to check as potential prefixes
35 s: Target string to check prefixes against
36
37 Returns:
38 Integer count of how many words are prefixes of s
39 """
40 # Use sum with generator expression to count True values
41 # s.startswith(w) returns True if w is a prefix of s, False otherwise
42 # sum() treats True as 1 and False as 0
43 return sum(s.startswith(word) for word in words)
44
1class Solution {
2 /**
3 * Counts the number of strings in the words array that are prefixes of string s.
4 *
5 * @param words Array of strings to check as potential prefixes
6 * @param s The target string to check prefixes against
7 * @return The count of strings from words array that are prefixes of s
8 */
9 public int countPrefixes(String[] words, String s) {
10 // Initialize counter for valid prefixes
11 int prefixCount = 0;
12
13 // Iterate through each word in the words array
14 for (String word : words) {
15 // Check if current word is a prefix of string s
16 if (s.startsWith(word)) {
17 // Increment counter if word is a valid prefix
18 prefixCount++;
19 }
20 }
21
22 // Return the total count of valid prefixes
23 return prefixCount;
24 }
25}
26
1class Solution {
2public:
3 int countPrefixes(vector<string>& words, string s) {
4 int count = 0;
5
6 // Iterate through each word in the words array
7 for (const auto& word : words) {
8 // Check if the current word is a prefix of string s
9 // starts_with() returns true if s begins with word, false otherwise
10 // The boolean result is implicitly converted to 1 (true) or 0 (false)
11 count += s.starts_with(word);
12 }
13
14 return count;
15 }
16};
17
1/**
2 * Counts the number of strings in the words array that are prefixes of string s
3 * @param words - Array of strings to check as potential prefixes
4 * @param s - Target string to check prefixes against
5 * @returns Number of strings from words array that are prefixes of s
6 */
7function countPrefixes(words: string[], s: string): number {
8 // Filter the words array to keep only strings that are prefixes of s
9 // Then return the count of filtered elements
10 return words.filter((word: string) => s.startsWith(word)).length;
11}
12
Time and Space Complexity
The time complexity is O(m × n)
, where m
is the number of words in the array and n
is the length of string s
. This is because:
- We iterate through all
m
words in the array - For each word, the
startswith()
method performs a character-by-character comparison which in the worst case compares up ton
characters (the length of strings
) - Therefore, the total time complexity is
O(m × n)
The space complexity is O(1)
as we only use a constant amount of extra space to store the count result. The generator expression in sum()
doesn't create an intermediate list but processes elements one by one, using only constant additional memory.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Empty String Edge Case
One common pitfall is not considering that an empty string ""
is technically a prefix of any string. If the words
array contains empty strings, they will be counted as valid prefixes.
Problem Example:
words = ["a", "", "abc"] s = "abcd" # Result: 3 (all three are prefixes, including "")
Solution: If empty strings should not be counted, add a length check:
return sum(s.startswith(w) and len(w) > 0 for w in words)
2. Case Sensitivity Confusion
While the problem states all strings contain only lowercase letters, in real-world scenarios, developers might forget that startswith()
is case-sensitive.
Problem Example:
words = ["A", "ab"] s = "abc" # "A" will NOT be counted as a prefix due to case difference
Solution: For case-insensitive matching (if needed):
return sum(s.lower().startswith(w.lower()) for w in words)
3. Performance with Large Datasets
When dealing with very long strings or many words, the repeated string prefix checking can become inefficient. Each startswith()
call performs character-by-character comparison.
Problem Example:
# If s has length 10^6 and words has 10^4 elements with average length 1000 # This results in many redundant character comparisons
Solution: For better performance with large datasets, consider using a Trie (prefix tree):
class TrieNode:
def __init__(self):
self.children = {}
self.is_end = False
def countPrefixes(words, s):
# Build trie from words
root = TrieNode()
for word in words:
node = root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.is_end = True
# Count prefixes by traversing s
count = 0
node = root
for char in s:
if char not in node.children:
break
node = node.children[char]
if node.is_end:
count += 1
return count
4. Assuming Word is Always Shorter Than s
Developers might assume all words in the array are shorter than s
, but startswith()
correctly handles cases where the word is longer than s
(returns False
).
Problem Example:
words = ["abc", "abcdef"] s = "abc" # "abcdef" is NOT a prefix of "abc" (correctly returns False) # Result: 1 (only "abc" is a prefix)
This is handled correctly by the default implementation, but it's important to understand this behavior when debugging or testing edge cases.
What are the most two important steps in writing a depth first search function? (Select 2)
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!