3034. Number of Subarrays That Match a Pattern I
Problem Description
You have two arrays: nums
(containing integers) and pattern
(containing only -1, 0, or 1). Your task is to find how many subarrays in nums
match the given pattern
.
A subarray of nums
with length m + 1
(where m
is the length of pattern
) matches the pattern if it follows these rules for each element in the pattern:
- When
pattern[k] = 1
: The next element in the subarray must be greater than the current element (nums[i + k + 1] > nums[i + k]
) - When
pattern[k] = 0
: The next element in the subarray must be equal to the current element (nums[i + k + 1] == nums[i + k]
) - When
pattern[k] = -1
: The next element in the subarray must be less than the current element (nums[i + k + 1] < nums[i + k]
)
The pattern essentially describes the relationship between consecutive elements in the subarray. For example, if pattern = [1, -1]
, you're looking for subarrays of length 3 where the second element is greater than the first (pattern[0] = 1), and the third element is less than the second (pattern[1] = -1).
The solution iterates through all possible starting positions in nums
where a subarray of the required length could begin. For each starting position i
, it checks if the relationships between consecutive elements match the pattern. The helper function f(a, b)
converts the relationship between two numbers into the pattern format (1 for increasing, 0 for equal, -1 for decreasing). If all relationships in the subarray match the pattern, the count is incremented.
Intuition
The key insight is that the pattern array is essentially encoding the relationships between consecutive elements in a subarray. Instead of looking at the actual values, we care about whether elements are increasing, decreasing, or staying the same.
Think of it this way: if we have a pattern [1, -1, 0]
, we're looking for subarrays where:
- Element 2 > Element 1 (going up)
- Element 3 < Element 2 (going down)
- Element 4 = Element 3 (staying same)
This naturally leads us to a brute force approach: check every possible subarray of the correct length. For each potential starting position i
in nums
, we examine the subarray from i
to i + m
(where m
is the pattern length).
To make the comparison cleaner, we create a helper function f(a, b)
that converts the relationship between two numbers into the same format as our pattern:
- Returns
1
ifa < b
(increasing) - Returns
0
ifa == b
(equal) - Returns
-1
ifa > b
(decreasing)
Now we can directly compare the relationships in our subarray with the pattern values. We iterate through each position in the pattern, compute the relationship between the corresponding consecutive elements in the subarray using f()
, and check if it matches the pattern value at that position.
The use of all()
function makes the code concise - it returns True
only if every relationship in the subarray matches the corresponding pattern value. This boolean result can be directly added to our answer counter (Python treats True
as 1 and False
as 0).
Solution Approach
The solution uses a straightforward enumeration approach to check all possible subarrays of length m + 1
in the nums
array.
Implementation Steps:
-
Define a helper function
f(a, b)
:- This function takes two numbers and returns their relationship in pattern format
- Returns
0
ifa == b
(equal) - Returns
1
ifa < b
(increasing) - Returns
-1
ifa > b
(decreasing)
-
Iterate through all valid starting positions:
- Loop from
i = 0
toi = len(nums) - len(pattern) - 1
- This ensures we have enough elements remaining to form a subarray of size
m + 1
- Loop from
-
Check each subarray against the pattern:
- For each starting position
i
, we need to verify if the subarraynums[i..i+m]
matches the pattern - We use
all()
with a generator expression to check all positions in the pattern - For each index
k
in the pattern (from 0 tom-1
):- Compare elements
nums[i + k]
andnums[i + k + 1]
- Apply function
f()
to get their relationship - Check if this relationship equals
pattern[k]
- Compare elements
- For each starting position
-
Count matching subarrays:
- The
all()
function returnsTrue
(treated as 1) if all relationships match - Returns
False
(treated as 0) if any relationship doesn't match - Add this boolean value directly to the answer counter
- The
Time Complexity: O(n * m)
where n
is the length of nums
and m
is the length of pattern
. We check n - m
possible starting positions, and for each position, we perform m
comparisons.
Space Complexity: O(1)
as we only use a constant amount of extra space for variables.
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 understand the solution approach.
Given:
nums = [3, 5, 2, 6, 6]
pattern = [1, -1, 0]
We need to find subarrays of length 4 (pattern length + 1) that match the pattern.
Step 1: Understanding the pattern
pattern[0] = 1
: second element > first elementpattern[1] = -1
: third element < second elementpattern[2] = 0
: fourth element = third element
Step 2: Check each possible starting position
Starting at i = 0: Subarray [3, 5, 2, 6]
- Compare
3
and5
:f(3, 5) = 1
(since 3 < 5) ✓ matchespattern[0] = 1
- Compare
5
and2
:f(5, 2) = -1
(since 5 > 2) ✓ matchespattern[1] = -1
- Compare
2
and6
:f(2, 6) = 1
(since 2 < 6) ✗ doesn't matchpattern[2] = 0
- Result: No match (count remains 0)
Starting at i = 1: Subarray [5, 2, 6, 6]
- Compare
5
and2
:f(5, 2) = -1
(since 5 > 2) ✗ doesn't matchpattern[0] = 1
- Result: No match (count remains 0)
Starting at i = 2: Would need subarray [2, 6, 6, ?]
but we only have 5 elements total
- This is our last valid starting position since we need 4 consecutive elements
Let's try another example where we find a match:
Given:
nums = [1, 3, 2, 2]
pattern = [1, -1, 0]
Starting at i = 0: Subarray [1, 3, 2, 2]
- Compare
1
and3
:f(1, 3) = 1
✓ matchespattern[0] = 1
- Compare
3
and2
:f(3, 2) = -1
✓ matchespattern[1] = -1
- Compare
2
and2
:f(2, 2) = 0
✓ matchespattern[2] = 0
- Result: Match found! (count = 1)
The algorithm would return 1 as the final answer.
Solution Implementation
1class Solution:
2 def countMatchingSubarrays(self, nums: List[int], pattern: List[int]) -> int:
3 """
4 Count the number of subarrays in nums that match the given pattern.
5 A subarray matches if the relationship between consecutive elements
6 follows the pattern (-1: decreasing, 0: equal, 1: increasing).
7
8 Args:
9 nums: List of integers to search within
10 pattern: List of -1, 0, or 1 representing the desired pattern
11
12 Returns:
13 Number of matching subarrays
14 """
15
16 def compare_elements(first: int, second: int) -> int:
17 """
18 Compare two elements and return their relationship.
19
20 Args:
21 first: First element to compare
22 second: Second element to compare
23
24 Returns:
25 0 if elements are equal
26 1 if first < second (increasing)
27 -1 if first > second (decreasing)
28 """
29 if first == second:
30 return 0
31 elif first < second:
32 return 1
33 else:
34 return -1
35
36 # Initialize counter for matching subarrays
37 matching_count = 0
38
39 # Iterate through all possible starting positions for subarrays
40 # Stop when remaining elements are fewer than pattern length + 1
41 for start_index in range(len(nums) - len(pattern)):
42 # Check if current subarray matches the pattern
43 # Compare each consecutive pair with corresponding pattern element
44 is_match = all(
45 compare_elements(nums[start_index + k], nums[start_index + k + 1]) == pattern_value
46 for k, pattern_value in enumerate(pattern)
47 )
48
49 # Increment counter if pattern matches (True converts to 1, False to 0)
50 matching_count += is_match
51
52 return matching_count
53
1class Solution {
2 /**
3 * Counts the number of subarrays in nums that match the given pattern.
4 * A subarray matches if consecutive element comparisons follow the pattern:
5 * - pattern[k] = 1: nums[i+k+1] > nums[i+k]
6 * - pattern[k] = 0: nums[i+k+1] == nums[i+k]
7 * - pattern[k] = -1: nums[i+k+1] < nums[i+k]
8 *
9 * @param nums The input array of integers
10 * @param pattern The pattern array containing values -1, 0, or 1
11 * @return The count of matching subarrays
12 */
13 public int countMatchingSubarrays(int[] nums, int[] pattern) {
14 int numsLength = nums.length;
15 int patternLength = pattern.length;
16 int matchCount = 0;
17
18 // Iterate through all possible starting positions for subarrays
19 for (int startIndex = 0; startIndex < numsLength - patternLength; startIndex++) {
20 int isMatching = 1; // Flag to track if current subarray matches (1 = true, 0 = false)
21
22 // Check if the current subarray matches the pattern
23 for (int patternIndex = 0; patternIndex < patternLength && isMatching == 1; patternIndex++) {
24 // Compare consecutive elements and check against pattern
25 if (f(nums[startIndex + patternIndex], nums[startIndex + patternIndex + 1]) != pattern[patternIndex]) {
26 isMatching = 0; // Pattern mismatch found
27 }
28 }
29
30 // Add to count if the subarray matches the pattern
31 matchCount += isMatching;
32 }
33
34 return matchCount;
35 }
36
37 /**
38 * Compares two integers and returns a comparison indicator.
39 *
40 * @param a The first integer
41 * @param b The second integer
42 * @return 0 if a == b, 1 if a < b, -1 if a > b
43 */
44 private int f(int a, int b) {
45 return a == b ? 0 : (a < b ? 1 : -1);
46 }
47}
48
1class Solution {
2public:
3 int countMatchingSubarrays(vector<int>& nums, vector<int>& pattern) {
4 int numsSize = nums.size();
5 int patternSize = pattern.size();
6 int matchCount = 0;
7
8 // Lambda function to compare two consecutive elements
9 // Returns: 0 if equal, 1 if first < second, -1 if first > second
10 auto compareElements = [](int first, int second) {
11 if (first == second) {
12 return 0;
13 }
14 return (first < second) ? 1 : -1;
15 };
16
17 // Iterate through all possible starting positions for subarrays
18 for (int startIndex = 0; startIndex < numsSize - patternSize; ++startIndex) {
19 bool isMatching = true;
20
21 // Check if the current subarray matches the pattern
22 for (int patternIndex = 0; patternIndex < patternSize && isMatching; ++patternIndex) {
23 // Compare the relationship between consecutive elements with the pattern
24 int currentComparison = compareElements(
25 nums[startIndex + patternIndex],
26 nums[startIndex + patternIndex + 1]
27 );
28
29 if (currentComparison != pattern[patternIndex]) {
30 isMatching = false;
31 }
32 }
33
34 // Increment count if the subarray matches the pattern
35 if (isMatching) {
36 matchCount++;
37 }
38 }
39
40 return matchCount;
41 }
42};
43
1/**
2 * Counts the number of subarrays in nums that match the given pattern.
3 * A subarray matches if the relationship between consecutive elements follows the pattern:
4 * - pattern[i] = 1: nums[i] < nums[i+1] (increasing)
5 * - pattern[i] = 0: nums[i] == nums[i+1] (equal)
6 * - pattern[i] = -1: nums[i] > nums[i+1] (decreasing)
7 *
8 * @param nums - The input array of numbers
9 * @param pattern - The pattern array containing values -1, 0, or 1
10 * @returns The count of matching subarrays
11 */
12function countMatchingSubarrays(nums: number[], pattern: number[]): number {
13 /**
14 * Helper function to compare two numbers and return the relationship pattern
15 * @param firstNum - The first number to compare
16 * @param secondNum - The second number to compare
17 * @returns 0 if equal, 1 if first < second, -1 if first > second
18 */
19 const getRelationshipPattern = (firstNum: number, secondNum: number): number => {
20 if (firstNum === secondNum) {
21 return 0;
22 }
23 return firstNum < secondNum ? 1 : -1;
24 };
25
26 const numsLength = nums.length;
27 const patternLength = pattern.length;
28 let matchingCount = 0;
29
30 // Iterate through all possible starting positions for subarrays
31 for (let startIndex = 0; startIndex < numsLength - patternLength; startIndex++) {
32 let isMatching = true;
33
34 // Check if the current subarray matches the pattern
35 for (let patternIndex = 0; patternIndex < patternLength && isMatching; patternIndex++) {
36 const currentRelation = getRelationshipPattern(
37 nums[startIndex + patternIndex],
38 nums[startIndex + patternIndex + 1]
39 );
40
41 // If the relationship doesn't match the pattern, mark as not matching
42 if (currentRelation !== pattern[patternIndex]) {
43 isMatching = false;
44 }
45 }
46
47 // Increment count if the subarray matches the pattern
48 if (isMatching) {
49 matchingCount++;
50 }
51 }
52
53 return matchingCount;
54}
55
Time and Space Complexity
Time Complexity: O(n × m)
, where n
is the length of the array nums
and m
is the length of the array pattern
.
The analysis breaks down as follows:
- The outer loop runs
len(nums) - len(pattern)
times, which is approximatelyn - m
iterations (orO(n)
in the worst case). - For each iteration of the outer loop, the
all()
function with the generator expression checksm
conditions (one for each element inpattern
). - Each condition involves calling function
f()
and comparing values, both of which areO(1)
operations. - Therefore, the total time complexity is
O((n - m) × m)
, which simplifies toO(n × m)
.
Space Complexity: O(1)
The space analysis:
- The function uses only a constant amount of extra space for variables
ans
and loop indicesi
andk
. - The generator expression in
all()
doesn't create a list but evaluates lazily, usingO(1)
space. - Function
f()
only uses local variables and returns a single integer. - No additional data structures are created that scale with input size.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Index Out of Bounds Error
One of the most common mistakes is incorrectly calculating the loop boundary, leading to index out of bounds errors when accessing nums[i + k + 1]
.
Incorrect Implementation:
# Wrong: This will cause index out of bounds
for i in range(len(nums) - len(pattern) + 1): # Off by one error
for k in range(len(pattern)):
if compare(nums[i + k], nums[i + k + 1]) != pattern[k]:
# When i = len(nums) - len(pattern),
# nums[i + k + 1] will exceed array bounds
Correct Implementation:
# Correct: Stop iteration at the right position
for i in range(len(nums) - len(pattern)):
# Now nums[i + k + 1] will never exceed bounds
2. Confusing Pattern Length with Subarray Length
The pattern describes relationships between consecutive elements, so a pattern of length m
requires a subarray of length m + 1
.
Common Mistake:
# Wrong: Treating pattern length as subarray length
subarray_length = len(pattern) # Incorrect!
for i in range(len(nums) - subarray_length + 1):
# This checks subarrays that are too short
Correct Understanding:
# Right: Subarray length is pattern length + 1
subarray_length = len(pattern) + 1
for i in range(len(nums) - len(pattern)): # Same as len(nums) - subarray_length + 1
# Checks subarrays of correct length
3. Incorrect Comparison Function Logic
Mixing up the return values for the comparison function can lead to wrong pattern matching.
Common Mistake:
def compare_elements(a, b):
if a == b:
return 0
elif a > b: # Wrong: This returns 1 for decreasing
return 1
else:
return -1
Correct Implementation:
def compare_elements(a, b):
if a == b:
return 0
elif a < b: # Correct: Returns 1 for increasing (a < b means b is greater)
return 1
else:
return -1
4. Edge Case: Empty Pattern
Not handling the edge case where pattern is empty can cause unexpected behavior.
Solution:
def countMatchingSubarrays(self, nums: List[int], pattern: List[int]) -> int:
# Handle edge case
if not pattern:
return len(nums) # Every single element is a valid subarray
# Continue with normal logic...
5. Performance Pitfall: Redundant Comparisons
Creating unnecessary lists or performing redundant operations inside the loop.
Inefficient:
for i in range(len(nums) - len(pattern)):
# Creating a new list unnecessarily
subarray = nums[i:i + len(pattern) + 1]
relationships = []
for j in range(len(subarray) - 1):
relationships.append(compare_elements(subarray[j], subarray[j + 1]))
if relationships == pattern:
matching_count += 1
Efficient:
for i in range(len(nums) - len(pattern)):
# Direct comparison without creating intermediate lists
is_match = all(
compare_elements(nums[i + k], nums[i + k + 1]) == pattern[k]
for k in range(len(pattern))
)
matching_count += is_match
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!