3176. Find the Maximum Length of a Good Subsequence I
Problem Description
You are given an integer array nums
and a non-negative integer k
. A sequence of integers seq
is called good if there are at most k
indices i
in the range [0, seq.length - 2]
such that seq[i] != seq[i + 1]
.
The task is to return the maximum possible length of a good subsequence of nums
.
Intuition
To solve this problem, we use a dynamic programming approach. We want to find the longest good subsequence from the given array, subject to the constraint that there are at most k
indices where consecutive elements differ.
The key is to use a 2D array f
where f[i][h]
represents the length of the longest good subsequence ending at index i
with no more than h
allowed differences between consecutive elements. Initially, we set f[i][h] = 1
, as the shortest subsequence can be a single element.
For each element nums[i]
, we check all previous elements nums[j]
where j < i
. If nums[i]
equals nums[j]
, we can extend any subsequence ending at j
without costing any additional allowed differences. Hence, we update f[i][h] = max(f[i][h], f[j][h] + 1)
.
If nums[i]
differs from nums[j]
, we can only extend the subsequence if we have some allowed differences left, i.e., if h > 0
. In this case, we update f[i][h] = max(f[i][h], f[j][h - 1] + 1)
.
Finally, the answer is the maximum length found across all possible subsequences that use up to k
allowed differences, which is max(f[i][k])
.
Learn more about Dynamic Programming patterns.
Solution Approach
The solution employs a dynamic programming approach to determine the maximum length of a good subsequence given the constraints. Here's how the solution is implemented:
-
Initialization:
- We start by determining the length of the input array
nums
and defining a 2D listf
wheref[i][h]
tracks the longest good subsequence ending at indexi
, withh
indicating the number of changes allowed. Each entry inf
is initialized to1
since the minimum subsequence length can be just one element.
- We start by determining the length of the input array
-
Iterating Through
nums
:- We iterate over each element
x
in the arraynums
using its indexi
. - For every element
x
, we consider subsequences ending at each prior elementy
indexed byj
.
- We iterate over each element
-
Updating the DP Table:
-
Same Element Extension: If
x
(current element) is equal toy
(previous element we are considering), then we can extend the subsequence ending atj
without using any of the allowedk
changes. Hence, we updatef[i][h] = max(f[i][h], f[j][h] + 1)
for the same number of changes. -
Different Element Extension: If
x
is different fromy
, we can still extend the subsequence if we have remaining allowed changes (h > 0
). In this case, updatef[i][h] = max(f[i][h], f[j][h - 1] + 1)
. This uses one of the allowed changes to accommodate the difference betweenx
andy
.
-
-
Finding the Result:
- After processing all elements and possible previous elements for each, the answer will be the maximum value in
f[i][k]
for any0 ≤ i < n
, which represents the longest sequence possible with up tok
allowed changes.
- After processing all elements and possible previous elements for each, the answer will be the maximum value in
This approach efficiently calculates the maximum length of a good subsequence by leveraging dynamic programming to balance potential subsequences and changes, providing the correct solution by exploring all possibilities within constraints.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's illustrate the solution approach with a small example.
Consider the input array nums = [1, 2, 2, 3, 1]
and k = 1
.
Step 1: Initialization
-
Create a 2D array
f
. Supposenums
has lengthn = 5
. The arrayf
has dimensions5 x 2
(sinceh
ranges from0
tok
, inclusive), initialized to1
:f = [ [1, 1], [1, 1], [1, 1], [1, 1], [1, 1] ]
Step 2: Iterating Through nums
-
For
i = 1
(nums[1] = 2):- Consider
j = 0
(nums[0] = 1):- Since
2 ≠ 1
, we can extend the subsequence ifh > 0
:- When
h = 1
: Updatef[1][1] = max(f[1][1], f[0][0] + 1) = max(1, 2) = 2
.
- When
- Since
- Consider
-
For
i = 2
(nums[2] = 2):- Consider
j = 0
andj = 1
:nums[2] == nums[1]
(2 == 2), so extend without additional change:f[2][0] = max(f[2][0], f[1][0] + 1) = max(1, 2) = 2
.f[2][1] = max(f[2][1], f[1][1] + 1) = max(1, 3) = 3
.
- Again,
2 ≠ 1
, we can extend using a change:f[2][1] = max(f[2][1], f[0][0] + 1) = max(3, 2) = 3
.
- Consider
-
For
i = 3
(nums[3] = 3):- Consider
j = 0
,j = 1
, andj = 2
:- Different value:
3 ≠ 2
, extend with changeh = 1
:- Update
f[3][1] = max(f[3][1], f[2][0] + 1) = max(1, 3) = 3
. - Update
f[3][1] = max(f[3][1], f[1][0] + 1) = max(3, 2) = 3
.
- Update
- Different value:
- Consider
-
For
i = 4
(nums[4] = 1):- Consider
j = 0
,j = 1
,j = 2
,j = 3
:- For
j = 0
(1 == 1):f[4][0] = max(f[4][0], f[0][0] + 1) = max(1, 2) = 2
. - Possible with change
h = 1
forj = 3
:f[4][1] = max(f[4][1], f[3][0] + 1) = max(1, 2) = 2
.
- For
- Consider
Step 3: Finding the Result
- The answer is the maximum value in any
f[i][k]
. Calculating,max(f[i][1])
for eachi
gives3
.
Thus, the maximum possible length of a good subsequence is 3
.
Solution Implementation
1from typing import List
2
3class Solution:
4 def maximumLength(self, nums: List[int], k: int) -> int:
5 n = len(nums) # Length of the input list
6 # Create a 2D list initialized to 1, dimensions are n x (k + 1)
7 dp = [[1] * (k + 1) for _ in range(n)]
8 result = 0 # Variable to store the result
9
10 # Iterate over each number in the list
11 for i, x in enumerate(nums):
12 # Iterate over possible allowed changes from 0 to k
13 for h in range(k + 1):
14 # Compare the current number with all previous numbers
15 for j, y in enumerate(nums[:i]):
16 if x == y:
17 # If the numbers are the same, extend the sequence without changes
18 dp[i][h] = max(dp[i][h], dp[j][h] + 1)
19 elif h > 0:
20 # If we can make a change, consider the sequence where this changed
21 dp[i][h] = max(dp[i][h], dp[j][h - 1] + 1)
22 # Update the result with the max sequence length found with at most k changes
23 result = max(result, dp[i][k])
24
25 return result
26
1class Solution {
2 public int maximumLength(int[] nums, int k) {
3 int n = nums.length; // Length of the input array
4 // Dynamic programming array to store max length of subsequence
5 int[][] dp = new int[n][k + 1];
6 int maxLength = 0; // Variable to hold the answer
7
8 // Iterate over each element in the array
9 for (int i = 0; i < n; ++i) {
10 // Iterate over allowed number of changes from 0 to k
11 for (int changes = 0; changes <= k; ++changes) {
12 // Check all previous elements for forming a non-decreasing subsequence
13 for (int j = 0; j < i; ++j) {
14 if (nums[i] == nums[j]) {
15 // If the current element is equal to the previous element
16 dp[i][changes] = Math.max(dp[i][changes], dp[j][changes]);
17 } else if (changes > 0) {
18 // If different and changes are allowed
19 dp[i][changes] = Math.max(dp[i][changes], dp[j][changes - 1]);
20 }
21 }
22 // Count current element in the subsequence length
23 ++dp[i][changes];
24 }
25 // Update the maximum length encountered
26 maxLength = Math.max(maxLength, dp[i][k]);
27 }
28 return maxLength;
29 }
30}
31
1#include <vector>
2#include <cstring>
3#include <algorithm>
4
5class Solution {
6public:
7 int maximumLength(std::vector<int>& nums, int k) {
8 int n = nums.size();
9 // Create a 2D array to store the function values
10 int dp[n][k + 1];
11 // Initialize the array to zero
12 std::memset(dp, 0, sizeof(dp));
13
14 int ans = 0; // Variable to store the maximum length
15
16 // Iterate over each element of nums
17 for (int i = 0; i < n; ++i) {
18 // Check for each count of elements removed (h) from 0 to k
19 for (int h = 0; h <= k; ++h) {
20 // Compare the current element with previous elements
21 for (int j = 0; j < i; ++j) {
22 if (nums[i] == nums[j]) {
23 // If they are the same, update dp[i][h]
24 dp[i][h] = std::max(dp[i][h], dp[j][h]);
25 } else if (h > 0) {
26 // If they are different and we can remove an element
27 dp[i][h] = std::max(dp[i][h], dp[j][h - 1]);
28 }
29 }
30 // Increment the path length for dp[i][h]
31 ++dp[i][h];
32 }
33 // Keep track of the maximum length found so far with k removals
34 ans = std::max(ans, dp[i][k]);
35 }
36
37 return ans; // Return the maximum subsequence length
38 }
39};
40
1// Function to find the maximum length of a subsequence with at most k swaps
2function maximumLength(nums: number[], k: number): number {
3 const n = nums.length; // Number of elements in the input array
4 // Create a 2D array 'f' to store intermediate maximum lengths
5 const f: number[][] = Array.from({ length: n }, () => Array(k + 1).fill(0));
6 let ans = 0; // Variable to store the result
7
8 // Iterate over each element in the input array
9 for (let i = 0; i < n; ++i) {
10 // Iterate through all possible swap counts from 0 to k
11 for (let h = 0; h <= k; ++h) {
12 // Check previous elements to find a longer subsequence
13 for (let j = 0; j < i; ++j) {
14 if (nums[i] === nums[j]) {
15 // If the current element is equal to any previous element, update the max without extra swap
16 f[i][h] = Math.max(f[i][h], f[j][h]);
17 } else if (h > 0) {
18 // If swaps are available and elements are different, consider adding one swap
19 f[i][h] = Math.max(f[i][h], f[j][h - 1]);
20 }
21 }
22 // Increment the maximum length for the current element and swap count
23 ++f[i][h];
24 }
25 // Update the answer with the maximum length for the current element with k swaps
26 ans = Math.max(ans, f[i][k]);
27 }
28
29 // Return the maximum length of the subsequence with at most k swaps
30 return ans;
31}
32
Time and Space Complexity
-
Time Complexity: The given code has a time complexity of
O(n^2 * k)
. This is because there are three nested loops:- The outer loop iterates
n
times through thenums
list. - The second loop iterates
k + 1
times, wherek
is the parameter given to the function. - The innermost loop iterates over a sublist of
nums
that can have up ton
elements, leading to a potentialn
iterations.
Combining these, the overall time complexity becomes
O(n^2 * k)
. - The outer loop iterates
-
Space Complexity: The space complexity of the algorithm is
O(n * k)
. This results from thef
matrix, which containsn
rows andk + 1
columns, resulting in a storage need ofO(n * (k + 1))
, which simplifies toO(n * k)
.
Learn more about how to find time and space complexity quickly.
How does merge sort divide the problem into subproblems?
Recommended Readings
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
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 algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!