3349. Adjacent Increasing Subarrays Detection I
Problem Description
You are given an array nums
of n
integers and an integer k
. Your task is to determine whether there exist two adjacent subarrays of length k
where both subarrays are strictly increasing.
The two subarrays must satisfy these conditions:
- Each subarray has exactly
k
elements - Both subarrays are strictly increasing (each element is greater than the previous one)
- The subarrays must be adjacent, meaning if the first subarray starts at index
a
, then the second subarray must start at indexb = a + k
(they don't overlap and are consecutive)
For example, if you have subarrays nums[a..a + k - 1]
and nums[b..b + k - 1]
, they are adjacent when b = a + k
.
Return true
if you can find two such adjacent strictly increasing subarrays of length k
, and false
otherwise.
Example visualization:
If nums = [1, 2, 3, 4, 5, 6]
and k = 3
:
- First subarray:
[1, 2, 3]
(indices 0-2) - strictly increasing ✓ - Second subarray:
[4, 5, 6]
(indices 3-5) - strictly increasing ✓ - They are adjacent (second starts right after first ends) ✓
- Result:
true
Intuition
The key insight is that we need to find the longest strictly increasing sequences in the array and check if we can extract two adjacent subarrays of length k
from them.
Think about it this way: if we have a long strictly increasing sequence, we might be able to split it into two parts of length k
each. For instance, a strictly increasing sequence of length 6
can be split into two adjacent subarrays of length 3
.
But there's another scenario: what if we have two separate strictly increasing sequences? If the first sequence ends right where the second begins, they form adjacent subarrays. For example, [1, 2, 3, 4, 2, 3, 4, 5]
has two increasing sequences [1, 2, 3, 4]
and [2, 3, 4, 5]
that meet at indices 3 and 4.
This leads us to track:
- The length of the current strictly increasing sequence (
cur
) - The length of the previous strictly increasing sequence (
pre
)
For each position, we check if the sequence continues to increase. When it breaks (or we reach the end):
- We can either split the current sequence in half to get two adjacent subarrays (hence
cur // 2
) - Or we can use the end of the previous sequence and the beginning of the current sequence (hence
min(pre, cur)
)
The maximum of these possibilities (mx
) tells us the longest possible length for two adjacent strictly increasing subarrays. If mx >= k
, we know we can find two adjacent subarrays of length k
.
The condition x >= nums[i + 1]
detects when the strictly increasing property breaks, signaling the end of a sequence and the potential start of a new one.
Solution Approach
The solution uses a single-pass algorithm with three variables to track increasing sequences:
cur
: Tracks the length of the current strictly increasing sequencepre
: Stores the length of the previous strictly increasing sequencemx
: Maintains the maximum possible length of two adjacent strictly increasing subarrays found so far
Algorithm walkthrough:
-
Initialize variables: Start with
mx = pre = cur = 0
-
Iterate through the array: For each element
x
at indexi
:- Increment
cur
by 1 (extending the current sequence)
- Increment
-
Check for sequence break: The sequence breaks when:
- We reach the last element (
i == len(nums) - 1
), OR - The current element is greater than or equal to the next element (
x >= nums[i + 1]
)
- We reach the last element (
-
When a sequence ends, calculate the maximum possible length of two adjacent subarrays:
- Option 1: Split the current sequence in half →
cur // 2
- Example: A sequence of length 6 can give us two adjacent subarrays of length 3
- Option 2: Use the tail of the previous sequence and the head of the current sequence →
min(pre, cur)
- Example: If
pre = 4
andcur = 3
, we can use the last 3 elements ofpre
and all 3 elements ofcur
- Example: If
- Update
mx
with the maximum of these options:mx = max(mx, cur // 2, min(pre, cur))
- Option 1: Split the current sequence in half →
-
Prepare for the next sequence:
- Set
pre = cur
(current becomes previous) - Reset
cur = 0
(start counting the new sequence)
- Set
-
Return the result: Check if
mx >= k
- If true, we can find two adjacent strictly increasing subarrays of length
k
- If false, no such pair exists
- If true, we can find two adjacent strictly increasing subarrays of length
Time Complexity: O(n)
- single pass through the array
Space Complexity: O(1)
- only using a few 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 the solution with nums = [2, 5, 7, 8, 9, 2, 3, 4, 3, 1]
and k = 3
.
We need to find if there exist two adjacent strictly increasing subarrays of length 3.
Initial state: mx = 0
, pre = 0
, cur = 0
Step-by-step iteration:
i | nums[i] | cur (before) | Action | cur (after) | pre | mx | Explanation |
---|---|---|---|---|---|---|---|
0 | 2 | 0 | cur++ | 1 | 0 | 0 | Start of array, building sequence |
1 | 5 | 1 | cur++ | 2 | 0 | 0 | 2 < 5, sequence continues |
2 | 7 | 2 | cur++ | 3 | 0 | 0 | 5 < 7, sequence continues |
3 | 8 | 3 | cur++ | 4 | 0 | 0 | 7 < 8, sequence continues |
4 | 9 | 4 | cur++ | 5 | 0 | 0 | 8 < 9, sequence continues |
5 | 2 | 5 | Break! | → | → | → | 9 ≥ 2, sequence breaks |
At break (i=5):
- Current sequence
[2,5,7,8,9]
has lengthcur = 5
- Calculate:
max(0, 5//2, min(0,5))
=max(0, 2, 0)
=2
- Update:
mx = 2
,pre = 5
,cur = 0
Continue iteration:
i | nums[i] | cur (before) | Action | cur (after) | pre | mx | Explanation |
---|---|---|---|---|---|---|---|
5 | 2 | 0 | cur++ | 1 | 5 | 2 | New sequence starts |
6 | 3 | 1 | cur++ | 2 | 5 | 2 | 2 < 3, sequence continues |
7 | 4 | 2 | cur++ | 3 | 5 | 2 | 3 < 4, sequence continues |
8 | 3 | 3 | Break! | → | → | → | 4 ≥ 3, sequence breaks |
At break (i=8):
- Current sequence
[2,3,4]
has lengthcur = 3
- Calculate:
max(2, 3//2, min(5,3))
=max(2, 1, 3)
=3
- Update:
mx = 3
,pre = 3
,cur = 0
Continue to end:
i | nums[i] | cur (before) | Action | cur (after) | pre | mx | Explanation |
---|---|---|---|---|---|---|---|
8 | 3 | 0 | cur++ | 1 | 3 | 3 | New sequence starts |
9 | 1 | 1 | End reached | → | → | → | Last element |
At end (i=9):
- Current sequence
[3,1]
has lengthcur = 1
- Calculate:
max(3, 1//2, min(3,1))
=max(3, 0, 1)
=3
- Final:
mx = 3
Result: Since mx = 3 >= k = 3
, return true
.
The algorithm found that we can have two adjacent strictly increasing subarrays of length 3. Looking back at our array, one valid pair would be:
- Subarray 1:
[5, 7, 8]
at indices 1-3 - Subarray 2:
[2, 3, 4]
at indices 5-7
These aren't adjacent in the original array, but the algorithm correctly identifies that the maximum possible length is 3, which comes from the sequence [2, 5, 7, 8, 9]
where we could take [5, 7, 8]
and the sequence [2, 3, 4]
that follows the break.
Solution Implementation
1class Solution:
2 def hasIncreasingSubarrays(self, nums: List[int], k: int) -> bool:
3 """
4 Check if there exist two adjacent strictly increasing subarrays of length k.
5
6 Args:
7 nums: Input array of integers
8 k: Required length of each subarray
9
10 Returns:
11 True if two adjacent strictly increasing subarrays of length k exist
12 """
13 # Track the maximum achievable length of valid adjacent subarrays
14 max_valid_length = 0
15
16 # Length of the previous strictly increasing subarray
17 previous_length = 0
18
19 # Length of the current strictly increasing subarray being built
20 current_length = 0
21
22 # Iterate through the array with index and value
23 for i, current_value in enumerate(nums):
24 # Extend current strictly increasing subarray
25 current_length += 1
26
27 # Check if we've reached the end of a strictly increasing sequence
28 # This happens when we're at the last element or next element is not greater
29 if i == len(nums) - 1 or current_value >= nums[i + 1]:
30 # Update maximum valid length considering two cases:
31 # 1. Split current subarray into two equal parts (current_length // 2)
32 # 2. Use previous and current as two adjacent subarrays (min of their lengths)
33 max_valid_length = max(
34 max_valid_length,
35 current_length // 2,
36 min(previous_length, current_length)
37 )
38
39 # Move to next sequence: current becomes previous, reset current
40 previous_length = current_length
41 current_length = 0
42
43 # Check if we can form two adjacent subarrays of at least length k
44 return max_valid_length >= k
45
1class Solution {
2 public boolean hasIncreasingSubarrays(List<Integer> nums, int k) {
3 // Track the maximum valid length found
4 int maxValidLength = 0;
5 // Length of the previous strictly increasing subarray
6 int previousLength = 0;
7 // Length of the current strictly increasing subarray being processed
8 int currentLength = 0;
9
10 int n = nums.size();
11
12 // Iterate through all elements in the list
13 for (int i = 0; i < n; i++) {
14 // Increment current subarray length
15 currentLength++;
16
17 // Check if we've reached the end of a strictly increasing subarray
18 // This happens when we're at the last element OR the next element is not greater
19 if (i == n - 1 || nums.get(i) >= nums.get(i + 1)) {
20 // Update maximum valid length considering two cases:
21 // 1. Using half of the current subarray (split into two equal parts)
22 // 2. Using adjacent subarrays (minimum of previous and current lengths)
23 maxValidLength = Math.max(maxValidLength,
24 Math.max(currentLength / 2,
25 Math.min(previousLength, currentLength)));
26
27 // Current subarray becomes the previous for next iteration
28 previousLength = currentLength;
29 // Reset current length for the next subarray
30 currentLength = 0;
31 }
32 }
33
34 // Check if we found any valid configuration with length at least k
35 return maxValidLength >= k;
36 }
37}
38
1class Solution {
2public:
3 bool hasIncreasingSubarrays(vector<int>& nums, int k) {
4 int maxValidLength = 0; // Maximum length of valid adjacent subarrays found
5 int previousLength = 0; // Length of the previous strictly increasing segment
6 int currentLength = 0; // Length of the current strictly increasing segment
7 int n = nums.size();
8
9 for (int i = 0; i < n; ++i) {
10 // Increment the current segment length
11 ++currentLength;
12
13 // Check if we've reached the end of a strictly increasing segment
14 // This happens when we're at the last element OR the next element is not greater
15 if (i == n - 1 || nums[i] >= nums[i + 1]) {
16 // Update the maximum valid length considering three cases:
17 // 1. Keep the current maximum
18 // 2. Split the current segment into two equal parts (currentLength / 2)
19 // 3. Use the minimum of previous and current segment lengths as adjacent pairs
20 maxValidLength = max({maxValidLength,
21 currentLength / 2,
22 min(previousLength, currentLength)});
23
24 // Move to the next segment
25 previousLength = currentLength;
26 currentLength = 0;
27 }
28 }
29
30 // Check if we found adjacent subarrays of at least length k
31 return maxValidLength >= k;
32 }
33};
34
1/**
2 * Checks if there exist two adjacent strictly increasing subarrays of length k
3 * @param nums - The input array of numbers
4 * @param k - The required length of each subarray
5 * @returns true if two adjacent strictly increasing subarrays of length k exist, false otherwise
6 */
7function hasIncreasingSubarrays(nums: number[], k: number): boolean {
8 // Track the maximum valid k value found, previous segment length, and current segment length
9 let maxValidK: number = 0;
10 let previousSegmentLength: number = 0;
11 let currentSegmentLength: number = 0;
12
13 const arrayLength: number = nums.length;
14
15 // Iterate through the array to find strictly increasing segments
16 for (let i = 0; i < arrayLength; ++i) {
17 // Increment current segment length
18 ++currentSegmentLength;
19
20 // Check if we've reached the end of array or found a non-increasing point
21 if (i === arrayLength - 1 || nums[i] >= nums[i + 1]) {
22 // Update maximum valid k value considering:
23 // 1. Half of current segment (for two adjacent subarrays within same segment)
24 // 2. Minimum of previous and current segment (for adjacent subarrays across segments)
25 maxValidK = Math.max(
26 maxValidK,
27 Math.floor(currentSegmentLength / 2),
28 Math.min(previousSegmentLength, currentSegmentLength)
29 );
30
31 // Move current segment to previous, reset current segment counter
32 previousSegmentLength = currentSegmentLength;
33 currentSegmentLength = 0;
34 }
35 }
36
37 // Return true if we found a valid configuration with length >= k
38 return maxValidK >= k;
39}
40
Time and Space Complexity
Time Complexity: O(n)
where n
is the length of the input array nums
. The algorithm iterates through the array exactly once using a single for loop. Each operation inside the loop (comparisons, arithmetic operations, and variable assignments) takes constant time O(1)
. Therefore, the overall time complexity is linear.
Space Complexity: O(1)
. The algorithm uses only a fixed number of variables (mx
, pre
, cur
, i
, and x
) regardless of the input size. No additional data structures that scale with the input are created. The space usage remains constant throughout the execution.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Misunderstanding "Adjacent" Subarrays
The Problem: Many developers initially interpret "adjacent" as overlapping or sharing elements. For instance, they might think subarrays [1,2,3]
and [2,3,4]
are adjacent because they share elements 2 and 3.
Why It Happens: The term "adjacent" in everyday language often means "next to each other" which could imply sharing boundaries.
The Correct Understanding: Adjacent means the two subarrays are consecutive without overlap. If the first subarray ends at index i
, the second must start at index i+1
.
Solution:
# WRONG: Checking overlapping subarrays
for i in range(len(nums) - k + 1):
first = nums[i:i+k]
second = nums[i+1:i+k+1] # This overlaps!
# CORRECT: Non-overlapping adjacent subarrays
for i in range(len(nums) - 2*k + 1):
first = nums[i:i+k]
second = nums[i+k:i+2*k] # Starts right after first ends
Pitfall 2: Incorrectly Calculating Maximum Valid Length
The Problem: Forgetting to consider that a single long increasing sequence can be split into two adjacent subarrays.
Why It Happens: The intuitive approach focuses on finding two separate increasing sequences and checking if they're adjacent, missing the case where one sequence contains both subarrays.
Example: With nums = [1,2,3,4,5,6]
and k = 3
, there's one increasing sequence of length 6 that can be split into [1,2,3]
and [4,5,6]
.
Solution:
# Always consider both options when a sequence ends:
max_valid_length = max(
max_valid_length,
current_length // 2, # Split single sequence
min(previous_length, current_length) # Use two sequences
)
Pitfall 3: Off-by-One Error in Sequence Break Detection
The Problem: Incorrectly determining when a strictly increasing sequence ends, particularly forgetting the "strictly" part (where equal values also break the sequence).
Why It Happens: Developers might check only nums[i] > nums[i+1]
instead of nums[i] >= nums[i+1]
.
Example: For [1,2,2,3,4,5]
with k = 2
, the sequence breaks at index 1 (where 2 >= 2
), not continuing through.
Solution:
# WRONG: Only checking for decrease
if i == len(nums) - 1 or current_value > nums[i + 1]:
# CORRECT: Checking for non-increase (includes equal values)
if i == len(nums) - 1 or current_value >= nums[i + 1]:
Pitfall 4: Not Handling Edge Cases
The Problem: Failing to handle arrays where k
is larger than half the array length or when k = 1
.
Why It Happens: The algorithm logic might seem to handle all cases, but special cases need verification.
Examples:
k = 1
: Any two consecutive elements form valid adjacent subarrays (since single elements are trivially "strictly increasing")k > len(nums) // 2
: Impossible to have two non-overlapping subarrays
Solution:
# Early return for edge cases
if k == 1:
return True # Any two elements work
if k > len(nums) // 2:
return False # Not enough space for two subarrays
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!