1967. Number of Strings That Appear as Substrings in Word
Problem Description
You are given an array of strings called patterns
and a single string called word
. Your task is to count how many strings from the patterns
array appear as substrings within word
.
A substring is defined as a contiguous sequence of characters that appears within another string. For example, "cat" is a substring of "concatenate" because the characters 'c', 'a', 't' appear consecutively in that order.
The solution iterates through each pattern p
in the patterns
array and checks if it exists as a substring in word
using Python's in
operator. The expression p in word
returns True
if p
is found as a substring in word
, and False
otherwise. The sum()
function counts the total number of True
values (since True
equals 1 and False
equals 0 in Python), giving us the final count of patterns that are substrings of word
.
For example, if patterns = ["a", "abc", "bc", "d"]
and word = "abc"
, the result would be 3 because "a", "abc", and "bc" all appear as substrings in "abc", while "d" does not.
Intuition
The problem asks us to find which patterns appear as substrings in a given word. The most straightforward approach is to check each pattern individually against the word.
Since we need to determine if one string is contained within another, we can leverage Python's built-in substring checking capability using the in
operator. This operator efficiently checks if a pattern exists anywhere within the word as a contiguous sequence.
The key insight is that we don't need to track positions or implement complex string matching algorithms. We simply need a yes/no answer for each pattern: "Does this pattern exist in the word?" Each positive match contributes 1 to our count.
By using a generator expression (p in word for p in patterns)
, we create a sequence of boolean values - True
for patterns that exist in the word and False
for those that don't. Since Python treats True
as 1 and False
as 0 in numeric contexts, we can directly sum these boolean values to get our final count. This eliminates the need for explicit counting logic or conditional statements.
This approach is both concise and efficient for the problem's requirements, as it leverages Python's optimized string searching implementation while maintaining clear, readable code.
Solution Approach
The implementation uses a simulation approach where we traverse through each string in the patterns
array and check if it exists as a substring in word
.
The algorithm follows these steps:
-
Iterate through patterns: We go through each string
p
in thepatterns
array one by one. -
Check substring existence: For each pattern
p
, we check if it appears as a substring inword
using Python'sin
operator. This operator performs an efficient substring search that returnsTrue
ifp
is found anywhere withinword
, andFalse
otherwise. -
Count matches: We use Python's
sum()
function to count the total number of patterns that exist as substrings. Since the expressionp in word
evaluates to a boolean value, and Python treatsTrue
as 1 andFalse
as 0 in numeric operations, summing these boolean values gives us the exact count we need.
The complete solution is implemented in a single line:
return sum(p in word for p in patterns)
This generator expression (p in word for p in patterns)
creates a sequence of boolean values for each pattern. The sum()
function then adds up all the True
values (counting as 1 each) to produce the final count.
The time complexity is O(n * m)
where n
is the number of patterns and m
is the average length of pattern strings multiplied by the length of word
, as each substring check operation takes time proportional to the lengths involved. The space complexity is O(1)
as we only maintain a running count without storing additional data structures.
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:
patterns = ["ab", "abc", "bc", "d", "c"]
word = "abc"
Step-by-step execution:
-
Check pattern "ab":
- Is "ab" in "abc"?
- Yes! "ab" appears at the beginning of "abc"
- This evaluates to
True
(counts as 1)
-
Check pattern "abc":
- Is "abc" in "abc"?
- Yes! "abc" matches the entire word
- This evaluates to
True
(counts as 1)
-
Check pattern "bc":
- Is "bc" in "abc"?
- Yes! "bc" appears at the end of "abc"
- This evaluates to
True
(counts as 1)
-
Check pattern "d":
- Is "d" in "abc"?
- No! The character "d" doesn't appear anywhere in "abc"
- This evaluates to
False
(counts as 0)
-
Check pattern "c":
- Is "c" in "abc"?
- Yes! "c" appears as the last character of "abc"
- This evaluates to
True
(counts as 1)
Final calculation:
The generator expression produces: [True, True, True, False, True]
When we apply sum()
to these boolean values:
sum([True, True, True, False, True])
=1 + 1 + 1 + 0 + 1
= 4
Therefore, 4 patterns from the array appear as substrings in the word "abc".
Solution Implementation
1class Solution:
2 def numOfStrings(self, patterns: List[str], word: str) -> int:
3 """
4 Count how many patterns are substrings of the given word.
5
6 Args:
7 patterns: List of pattern strings to check
8 word: The target string to search within
9
10 Returns:
11 Number of patterns that exist as substrings in word
12 """
13 # Initialize counter for matching patterns
14 count = 0
15
16 # Iterate through each pattern in the list
17 for pattern in patterns:
18 # Check if current pattern is a substring of word
19 if pattern in word:
20 count += 1
21
22 return count
23
1class Solution {
2 /**
3 * Counts the number of pattern strings that are substrings of the given word.
4 *
5 * @param patterns Array of pattern strings to search for
6 * @param word The word to search within
7 * @return The count of patterns that appear as substrings in word
8 */
9 public int numOfStrings(String[] patterns, String word) {
10 // Initialize counter for matching patterns
11 int matchCount = 0;
12
13 // Iterate through each pattern in the patterns array
14 for (String pattern : patterns) {
15 // Check if the current pattern is a substring of word
16 if (word.contains(pattern)) {
17 // Increment counter if pattern is found in word
18 matchCount++;
19 }
20 }
21
22 // Return the total count of matching patterns
23 return matchCount;
24 }
25}
26
1class Solution {
2public:
3 int numOfStrings(vector<string>& patterns, string word) {
4 int count = 0;
5
6 // Iterate through each pattern in the patterns array
7 for (const auto& pattern : patterns) {
8 // Check if the current pattern is a substring of word
9 // find() returns string::npos if pattern is not found
10 if (word.find(pattern) != string::npos) {
11 count++;
12 }
13 }
14
15 return count;
16 }
17};
18
1/**
2 * Counts the number of pattern strings that are substrings of the given word
3 * @param patterns - Array of pattern strings to check
4 * @param word - The word to search within for pattern occurrences
5 * @returns The count of patterns that appear as substrings in word
6 */
7function numOfStrings(patterns: string[], word: string): number {
8 // Filter patterns array to keep only those that exist as substrings in word
9 // Then return the length of the filtered array
10 return patterns.filter((pattern: string) => word.includes(pattern)).length;
11}
12
Time and Space Complexity
The time complexity is O(n × m × k)
, where n
is the number of patterns in the list, m
is the length of the word, and k
is the average length of each pattern. This is because:
- We iterate through
n
patterns - For each pattern, the
in
operator performs substring matching which takesO(m × k)
time in the worst case (checking each position in word as a potential start for the pattern)
The space complexity is O(1)
as we only use a constant amount of extra space for the sum operation. The generator expression (p in word for p in patterns)
doesn't create an intermediate list but yields values one at a time.
Note: The reference answer simplifies the time complexity to O(n × m)
by treating the pattern lengths as part of n
or considering them constant, which is a valid simplification when analyzing the dominant factors.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Counting Duplicate Occurrences Instead of Unique Patterns
A common misunderstanding is thinking we need to count how many times each pattern appears in the word, rather than just checking if it exists at least once.
Incorrect approach:
def numOfStrings(self, patterns: List[str], word: str) -> int:
count = 0
for pattern in patterns:
# Wrong: This counts occurrences, not just existence
count += word.count(pattern)
return count
Why it's wrong: If patterns = ["a"]
and word = "aaa"
, this would return 3 instead of 1. The problem asks for how many patterns are substrings, not how many times they appear.
Correct approach: Use the boolean check pattern in word
which only verifies existence.
2. Handling Empty Patterns
Empty strings are valid substrings of any string in Python. If the patterns array contains empty strings, they should be counted.
Potential issue:
def numOfStrings(self, patterns: List[str], word: str) -> int:
count = 0
for pattern in patterns:
# Some might incorrectly filter out empty patterns
if pattern and pattern in word: # Wrong filtering!
count += 1
return count
Why it matters: If patterns = ["", "a"]
and word = "abc"
, the result should be 2 (both "" and "a" are substrings), not 1.
Solution: Don't add unnecessary filters. Python's in
operator correctly handles empty strings:
"" in "any_string" # Returns True
3. Case Sensitivity Assumptions
The problem doesn't specify case-insensitive matching, so patterns should match exactly as they appear.
Incorrect assumption:
def numOfStrings(self, patterns: List[str], word: str) -> int:
word_lower = word.lower()
return sum(p.lower() in word_lower for p in patterns)
Why it's wrong: If patterns = ["A", "a"]
and word = "abc"
, only "a" should match, giving a count of 1, not 2.
Solution: Stick to exact matching without case conversion unless explicitly required.
4. Counting Duplicate Patterns Multiple Times
If the patterns array contains duplicates, each occurrence should be checked independently.
Example scenario:
patterns = ["a", "a", "b"]
andword = "abc"
- Result should be 3 (both "a"s count separately plus "b")
Incorrect approach:
def numOfStrings(self, patterns: List[str], word: str) -> int:
# Wrong: Using set removes duplicates
unique_patterns = set(patterns)
return sum(p in word for p in unique_patterns)
Correct approach: Process each pattern in the original list without deduplication.
How many times is a tree node visited in a depth first search?
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!