1566. Detect Pattern of Length M Repeated K or More Times
Problem Description
You are given an array of positive integers arr
and need to determine if there exists a pattern of length m
that repeats at least k
times consecutively.
A pattern is defined as a subarray (consecutive elements) that repeats multiple times in succession without any gaps or overlaps. The pattern has a specific length m
and must appear consecutively at least k
times.
For example:
- In array
[1, 2, 1, 2, 1, 3]
withm = 2
andk = 2
, the pattern[1, 2]
appears twice consecutively starting at index 0, so it returnstrue
. - In array
[1, 2, 3, 1, 2]
withm = 2
andk = 2
, although[1, 2]
appears twice, they are not consecutive (there's a3
in between), so it returnsfalse
.
The function should return true
if such a pattern exists, otherwise return false
.
The key insight is that for a pattern of length m
to repeat k
times, we need m × k
consecutive elements where each element at position i
equals the element at position i - m
. This means each element matches the corresponding element from the previous pattern repetition.
Intuition
The key observation is that if a pattern of length m
repeats k
times consecutively, then elements that are exactly m
positions apart must be equal.
Think about it this way: if we have a repeating pattern like [a, b, c, a, b, c, a, b, c]
where the pattern [a, b, c]
(length 3) repeats 3 times, then:
- Element at index 0 (
a
) equals element at index 3 (a
) - Element at index 1 (
b
) equals element at index 4 (b
) - Element at index 2 (
c
) equals element at index 5 (c
) - And so on...
This means each element should match the element that's exactly m
positions before it (except for the first m
elements which form the initial pattern).
To detect k
consecutive repetitions of a pattern of length m
, we need to find (k-1) × m
consecutive matches. Why (k-1) × m
and not k × m
? Because:
- The first occurrence of the pattern takes up
m
elements - Each additional repetition requires
m
more matching elements - So for
k
total repetitions, we need the firstm
elements plus(k-1) × m
matching elements
We can track this by maintaining a counter that increments each time we find arr[i] == arr[i-m]
. When this counter reaches (k-1) × m
, we've found our pattern. If at any point arr[i] != arr[i-m]
, the pattern is broken and we reset the counter to 0, as we need consecutive matches for a valid repeating pattern.
Solution Approach
The implementation follows a single-pass traversal approach with early termination optimization:
1. Initial Check:
First, we verify if the array has enough elements to potentially contain the pattern. If len(arr) < m * k
, it's impossible to have a pattern of length m
repeating k
times, so we return false
immediately.
2. Setting up Variables:
cnt
: A counter to track consecutive matches between elements that arem
positions aparttarget = (k - 1) * m
: The number of consecutive matches we need to confirmk
repetitions of a pattern of lengthm
3. Main Traversal:
Starting from index m
(since we need to compare with elements m
positions back), we iterate through the array:
for i in range(m, len(arr)):
if arr[i] == arr[i - m]:
cnt += 1
if cnt == target:
return True
else:
cnt = 0
For each element at position i
:
-
If
arr[i] == arr[i - m]
: This means the current element matches the elementm
positions before it, indicating the pattern might be continuing. We incrementcnt
.- If
cnt
reachestarget
, we've found(k-1) * m
consecutive matches, confirmingk
repetitions of a pattern of lengthm
, so we returntrue
.
- If
-
If
arr[i] != arr[i - m]
: The pattern is broken at this position. We resetcnt
to 0 and continue looking for a new potential pattern.
4. Final Return:
If we complete the traversal without finding the required pattern, we return false
.
The algorithm runs in O(n)
time complexity where n
is the length of the array, and uses O(1)
extra space, making it highly efficient.
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 the algorithm with a concrete example to see how it detects repeating patterns.
Example: arr = [2, 7, 2, 7, 2, 7, 5]
, m = 2
, k = 3
We're looking for a pattern of length 2 that repeats 3 times consecutively.
Step 1: Initial Setup
- Array length: 7
- Check if possible:
7 >= 2 * 3 = 6
✓ (enough elements) - Initialize
cnt = 0
- Calculate
target = (3 - 1) * 2 = 4
(we need 4 consecutive matches)
Step 2: Traverse Starting from Index m=2
i | arr[i] | arr[i-2] | Match? | cnt | Action |
---|---|---|---|---|---|
2 | 2 | 2 | ✓ | 1 | Increment cnt |
3 | 7 | 7 | ✓ | 2 | Increment cnt |
4 | 2 | 2 | ✓ | 3 | Increment cnt |
5 | 7 | 7 | ✓ | 4 | cnt == target, return true |
Pattern Found! The pattern [2, 7]
repeats 3 times consecutively:
- First occurrence: indices 0-1:
[2, 7]
- Second occurrence: indices 2-3:
[2, 7]
- Third occurrence: indices 4-5:
[2, 7]
Counter-Example: arr = [2, 7, 2, 5, 2, 7]
, m = 2
, k = 3
Same setup: cnt = 0
, target = 4
i | arr[i] | arr[i-2] | Match? | cnt | Action |
---|---|---|---|---|---|
2 | 2 | 2 | ✓ | 1 | Increment cnt |
3 | 5 | 7 | ✗ | 0 | Reset cnt to 0 |
4 | 2 | 2 | ✓ | 1 | Increment cnt |
5 | 7 | 5 | ✗ | 0 | Reset cnt to 0 |
No Pattern Found! Although [2, 7]
appears twice, the pattern breaks at index 3 where we have 5
instead of 7
. The algorithm correctly returns false
because we never reach the target of 4 consecutive matches.
Solution Implementation
1class Solution:
2 def containsPattern(self, arr: List[int], m: int, k: int) -> bool:
3 """
4 Check if array contains a pattern of length m repeated k times consecutively.
5
6 Args:
7 arr: Input array of integers
8 m: Length of the pattern
9 k: Number of times the pattern should repeat
10
11 Returns:
12 True if pattern exists, False otherwise
13 """
14 # Early return if array is too small to contain the pattern
15 if len(arr) < m * k:
16 return False
17
18 # Initialize counter for consecutive matches and calculate target matches needed
19 consecutive_matches = 0
20 # We need (k-1)*m matches because if we have k repetitions of pattern of length m,
21 # there are (k-1)*m positions where arr[i] == arr[i-m]
22 target_matches = (k - 1) * m
23
24 # Start from index m and compare each element with element m positions before
25 for i in range(m, len(arr)):
26 if arr[i] == arr[i - m]:
27 # Elements match the pattern, increment counter
28 consecutive_matches += 1
29
30 # Check if we've found enough consecutive matches
31 if consecutive_matches == target_matches:
32 return True
33 else:
34 # Pattern broken, reset counter
35 consecutive_matches = 0
36
37 # No valid pattern found
38 return False
39
1class Solution {
2 /**
3 * Checks if the array contains a pattern of length m repeated k consecutive times.
4 *
5 * @param arr the input array to check for patterns
6 * @param m the length of the pattern
7 * @param k the number of times the pattern should repeat consecutively
8 * @return true if such a pattern exists, false otherwise
9 */
10 public boolean containsPattern(int[] arr, int m, int k) {
11 // Early return if array is too small to contain the required pattern
12 if (arr.length < m * k) {
13 return false;
14 }
15
16 // Counter for consecutive matching elements at distance m
17 int consecutiveMatches = 0;
18
19 // Target number of consecutive matches needed to confirm pattern
20 // We need (k-1)*m matches because k repetitions of length m pattern
21 // requires (k-1)*m consecutive position matches
22 int targetMatches = (k - 1) * m;
23
24 // Iterate through array starting from index m
25 for (int i = m; i < arr.length; i++) {
26 // Check if current element matches the element m positions before
27 if (arr[i] == arr[i - m]) {
28 // Increment counter for consecutive matches
29 consecutiveMatches++;
30
31 // Check if we've found enough consecutive matches
32 if (consecutiveMatches == targetMatches) {
33 return true;
34 }
35 } else {
36 // Reset counter if pattern breaks
37 consecutiveMatches = 0;
38 }
39 }
40
41 // No valid pattern found
42 return false;
43 }
44}
45
1class Solution {
2public:
3 bool containsPattern(vector<int>& arr, int m, int k) {
4 // Early return if array is too small to contain the pattern
5 if (arr.size() < m * k) {
6 return false;
7 }
8
9 // Initialize counter for consecutive matching elements
10 int consecutiveMatches = 0;
11
12 // Calculate the target number of consecutive matches needed
13 // We need (k-1) complete patterns worth of matches
14 int targetMatches = (k - 1) * m;
15
16 // Iterate through array starting from index m
17 for (int i = m; i < arr.size(); ++i) {
18 // Check if current element matches the element m positions back
19 if (arr[i] == arr[i - m]) {
20 // Increment counter for consecutive matches
21 consecutiveMatches++;
22
23 // If we've reached the target, pattern exists
24 if (consecutiveMatches == targetMatches) {
25 return true;
26 }
27 } else {
28 // Reset counter if pattern breaks
29 consecutiveMatches = 0;
30 }
31 }
32
33 // No valid pattern found
34 return false;
35 }
36};
37
1/**
2 * Checks if the array contains a pattern of length m repeated k consecutive times
3 * @param arr - The input array of numbers to check
4 * @param m - The length of the pattern to find
5 * @param k - The number of consecutive times the pattern should repeat
6 * @returns true if pattern exists, false otherwise
7 */
8function containsPattern(arr: number[], m: number, k: number): boolean {
9 // Early return if array is too small to contain the pattern
10 if (arr.length < m * k) {
11 return false;
12 }
13
14 // Calculate the target number of consecutive matches needed
15 // We need (k-1) * m matches because k repetitions of pattern length m
16 // requires (k-1) * m consecutive matching elements
17 const targetMatchCount: number = (k - 1) * m;
18
19 // Counter for consecutive matching elements
20 let consecutiveMatches: number = 0;
21
22 // Start from index m and compare with elements m positions back
23 for (let i: number = m; i < arr.length; i++) {
24 // Check if current element matches the element m positions before
25 if (arr[i] === arr[i - m]) {
26 // Increment counter and check if we've found enough matches
27 consecutiveMatches++;
28 if (consecutiveMatches === targetMatchCount) {
29 return true;
30 }
31 } else {
32 // Reset counter if pattern is broken
33 consecutiveMatches = 0;
34 }
35 }
36
37 return false;
38}
39
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array arr
. The algorithm iterates through the array once starting from index m
to the end of the array. Each iteration performs constant time operations: comparing two array elements (arr[i] == arr[i - m]
), incrementing or resetting a counter, and checking if the counter equals the target value. Since we traverse the array once with constant operations per element, the overall time complexity is linear.
The space complexity is O(1)
. The algorithm only uses a fixed amount of extra space regardless of the input size. It maintains three variables: cnt
(counter), target
(calculated as (k - 1) * m
), and the loop variable i
. These variables occupy constant space that doesn't scale with the input array size, resulting in constant space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Target Calculation
Pitfall: A common mistake is incorrectly calculating how many consecutive matches are needed. Some might think we need k * m
matches instead of (k - 1) * m
.
Why it happens: The confusion arises from thinking about total elements versus transitions between patterns. If we have k
repetitions of a pattern of length m
, there are k * m
total elements, but only (k - 1) * m
positions where we compare elements that are m
positions apart.
Example: For pattern [1, 2]
repeated 3 times: [1, 2, 1, 2, 1, 2]
- Total elements: 3 × 2 = 6
- Comparisons: arr[2]==arr[0], arr[3]==arr[1], arr[4]==arr[2], arr[5]==arr[3] → 4 matches = (3-1) × 2
Solution: Remember that we're counting transitions, not total elements. The formula (k - 1) * m
represents the number of "links" between k consecutive patterns.
2. Not Resetting the Counter Properly
Pitfall: Forgetting to reset the counter to 0 when a mismatch occurs, or incorrectly trying to preserve partial matches.
Wrong approach:
if arr[i] != arr[i - m]: consecutive_matches -= 1 # Wrong! Should reset to 0
Why it's wrong: Pattern matching requires ALL elements to match consecutively. A single mismatch means we need to start fresh. There's no concept of "partial credit" in this problem.
Solution: Always reset to 0 on mismatch. The pattern must be perfectly consecutive without any breaks.
3. Off-by-One Error in Loop Starting Point
Pitfall: Starting the loop from the wrong index, such as range(0, len(arr))
or range(m-1, len(arr))
.
Why it happens: Confusion about when we can first make a comparison. We need at least m
elements before we can compare with an element m
positions back.
Correct approach: Start from index m
because:
- At index
m
, we can first comparearr[m]
witharr[0]
- This gives us access to elements from both the potential first and second pattern occurrences
Solution: Always start the loop at index m
: for i in range(m, len(arr))
4. Incorrect Early Termination Check
Pitfall: Using the wrong condition for the array size check, such as len(arr) <= m * k
or len(arr) < m + k
.
Why it's wrong:
len(arr) <= m * k
would reject valid cases where length exactly equalsm * k
len(arr) < m + k
uses addition instead of multiplication, which is mathematically incorrect
Solution: Use len(arr) < m * k
because we need exactly m * k
elements to have k
complete repetitions of a pattern of length m
.
What does the following code do?
1def f(arr1, arr2):
2 i, j = 0, 0
3 new_arr = []
4 while i < len(arr1) and j < len(arr2):
5 if arr1[i] < arr2[j]:
6 new_arr.append(arr1[i])
7 i += 1
8 else:
9 new_arr.append(arr2[j])
10 j += 1
11 new_arr.extend(arr1[i:])
12 new_arr.extend(arr2[j:])
13 return new_arr
14
1public static List<Integer> f(int[] arr1, int[] arr2) {
2 int i = 0, j = 0;
3 List<Integer> newArr = new ArrayList<>();
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.add(arr1[i]);
8 i++;
9 } else {
10 newArr.add(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.add(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.add(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
1function f(arr1, arr2) {
2 let i = 0, j = 0;
3 let newArr = [];
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.push(arr1[i]);
8 i++;
9 } else {
10 newArr.push(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.push(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.push(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
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!