Facebook Pixel

3034. Number of Subarrays That Match a Pattern I

MediumArrayString MatchingHash FunctionRolling Hash
Leetcode Link

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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 if a < b (increasing)
  • Returns 0 if a == b (equal)
  • Returns -1 if a > 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:

  1. Define a helper function f(a, b):

    • This function takes two numbers and returns their relationship in pattern format
    • Returns 0 if a == b (equal)
    • Returns 1 if a < b (increasing)
    • Returns -1 if a > b (decreasing)
  2. Iterate through all valid starting positions:

    • Loop from i = 0 to i = len(nums) - len(pattern) - 1
    • This ensures we have enough elements remaining to form a subarray of size m + 1
  3. Check each subarray against the pattern:

    • For each starting position i, we need to verify if the subarray nums[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 to m-1):
      • Compare elements nums[i + k] and nums[i + k + 1]
      • Apply function f() to get their relationship
      • Check if this relationship equals pattern[k]
  4. Count matching subarrays:

    • The all() function returns True (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

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 Evaluator

Example 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 element
  • pattern[1] = -1: third element < second element
  • pattern[2] = 0: fourth element = third element

Step 2: Check each possible starting position

Starting at i = 0: Subarray [3, 5, 2, 6]

  • Compare 3 and 5: f(3, 5) = 1 (since 3 < 5) ✓ matches pattern[0] = 1
  • Compare 5 and 2: f(5, 2) = -1 (since 5 > 2) ✓ matches pattern[1] = -1
  • Compare 2 and 6: f(2, 6) = 1 (since 2 < 6) ✗ doesn't match pattern[2] = 0
  • Result: No match (count remains 0)

Starting at i = 1: Subarray [5, 2, 6, 6]

  • Compare 5 and 2: f(5, 2) = -1 (since 5 > 2) ✗ doesn't match pattern[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 and 3: f(1, 3) = 1 ✓ matches pattern[0] = 1
  • Compare 3 and 2: f(3, 2) = -1 ✓ matches pattern[1] = -1
  • Compare 2 and 2: f(2, 2) = 0 ✓ matches pattern[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 approximately n - m iterations (or O(n) in the worst case).
  • For each iteration of the outer loop, the all() function with the generator expression checks m conditions (one for each element in pattern).
  • Each condition involves calling function f() and comparing values, both of which are O(1) operations.
  • Therefore, the total time complexity is O((n - m) × m), which simplifies to O(n × m).

Space Complexity: O(1)

The space analysis:

  • The function uses only a constant amount of extra space for variables ans and loop indices i and k.
  • The generator expression in all() doesn't create a list but evaluates lazily, using O(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
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

How many times is a tree node visited in a depth first search?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More