LeetCode 3202. Find the Maximum Length of Valid Subsequence II - DP(Med, Java, O(kn))


Problem Statement
Given an integer array nums
and a positive integer k
, find the maximum length of a valid subsequence. A subsequence is valid if all consecutive pairs in the subsequence have the same sum modulo k
. Formally, for a subsequence sub
of length x
:
(sub[0] + sub[1]) % k == (sub[1] + sub[2]) % k == ... == (sub[x-2] + sub[x-1]) % k
Example 1:
- Input:
nums = [1,2,3,4,5], k = 2
- Output:
5
- Explanation: The subsequence
[1,2,3,4,5]
is valid because(1+2)%2 = (2+3)%2 = (3+4)%2 = (4+5)%2 = 1
Example 2:
- Input:
nums = [1,4,2,3,1,4], k = 3
- Output:
4
- Explanation: The subsequence
[1,4,1,4]
is valid because(1+4)%3 = (4+1)%3 = (1+4)%3 = 2
Algorithm Walkthrough
Key Insight
For a valid subsequence where all consecutive pairs sum to the same value target
modulo k
, the remainders must follow an alternating pattern. If we have r1 + r2 ≡ target (mod k)
, then the subsequence alternates between remainders r1
and r2
.
Algorithm Steps
Handle Special Case: If
k = 1
, all numbers have remainder 0, so any subsequence is valid. Return the entire array length.Preprocess Remainders: Convert all numbers to their remainders modulo
k
for easier processing.Try All Target Sums: For each possible target sum
i
from 0 tok-1
:- Use dynamic programming where
dp[r]
represents the maximum length of subsequence ending with remainderr
and having target sumi
- Use dynamic programming where
DP Transition: For each element with remainder
arr[j]
:- To achieve target sum
i
, the previous remainder must be(i - arr[j] + k) % k
- Update:
dp[arr[j]] = dp[(i - arr[j] + k) % k] + 1
- To achieve target sum
Track Maximum: Keep track of the maximum length found across all target sums.
Detailed Example
For nums = [1,2,3,4,5], k = 2
:
- Remainders:
[1,0,1,0,1]
- When target sum
i = 1
:- Process
1
(remainder 1): previous should be(1-1+2)%2 = 0
,dp[1] = dp[0] + 1 = 1
- Process
2
(remainder 0): previous should be(1-0+2)%2 = 1
,dp[0] = dp[1] + 1 = 2
- Process
3
(remainder 1): previous should be(1-1+2)%2 = 0
,dp[1] = dp[0] + 1 = 3
- Process
4
(remainder 0): previous should be(1-0+2)%2 = 1
,dp[0] = dp[1] + 1 = 4
- Process
5
(remainder 1): previous should be(1-1+2)%2 = 0
,dp[1] = dp[0] + 1 = 5
- Process
Complexity Analysis
Time Complexity:
O(k × n)
wheren
is the length of the array. We iterate through allk
possible target sums, and for each target sum, we process alln
elements once.Space Complexity:
O(k)
for the DP array that stores the maximum length ending with each remainder, plusO(n)
for the preprocessed remainder array, giving usO(n + k)
total space.
Full Solution (Java)
The key insight of this solution is recognizing that valid subsequences follow alternating remainder patterns. By trying all possible target sums and using dynamic programming to track the maximum length ending with each remainder, we efficiently find the optimal subsequence without having to explicitly construct it.
class Solution {
public int maximumLength(int[] nums, int k) {
int length = nums.length;
// Special case: if k = 1, all numbers have remainder 0
// Any subsequence is valid since (0 + 0) % 1 = 0
if (k == 1) {
return length;
}
// Initialize result with minimum possible length (2 elements)
int res = 2;
// Preprocess: convert all numbers to their remainders mod k
int[] arr = new int[length];
for (int i = 0; i < length; i++) {
arr[i] = nums[i] % k;
}
// Try all possible target sums from 0 to k-1
for (int targetSum = 0; targetSum < k; targetSum++) {
// dp[r] = maximum length of valid subsequence ending with remainder r
// and having consecutive pairs that sum to targetSum mod k
int[] dp = new int[k];
// Process each element in the array
for (int j = 0; j < length; j++) {
int currentRemainder = arr[j];
// To achieve targetSum, what should the previous remainder be?
// If prev + current ≡ targetSum (mod k)
// Then prev ≡ targetSum - current (mod k)
int requiredPrevRemainder = (targetSum - currentRemainder + k) % k;
// Extend the subsequence ending with requiredPrevRemainder
dp[currentRemainder] = dp[requiredPrevRemainder] + 1;
// Update global maximum
res = Math.max(res, dp[currentRemainder]);
}
}
return res;
}
}
// Example trace for nums = [1,2,3,4,5], k = 2
/*
Remainders: [1,0,1,0,1]
Target sum = 0:
- Process 1 (rem=1): prev should be (0-1+2)%2=1, dp[1] = dp[1] + 1 = 1
- Process 2 (rem=0): prev should be (0-0+2)%2=0, dp[0] = dp[0] + 1 = 1
- Process 3 (rem=1): prev should be (0-1+2)%2=1, dp[1] = dp[1] + 1 = 2
- Process 4 (rem=0): prev should be (0-0+2)%2=0, dp[0] = dp[0] + 1 = 2
- Process 5 (rem=1): prev should be (0-1+2)%2=1, dp[1] = dp[1] + 1 = 3
Max so far: 3
Target sum = 1:
- Process 1 (rem=1): prev should be (1-1+2)%2=0, dp[1] = dp[0] + 1 = 1
- Process 2 (rem=0): prev should be (1-0+2)%2=1, dp[0] = dp[1] + 1 = 2
- Process 3 (rem=1): prev should be (1-1+2)%2=0, dp[1] = dp[0] + 1 = 3
- Process 4 (rem=0): prev should be (1-0+2)%2=1, dp[0] = dp[1] + 1 = 4
- Process 5 (rem=1): prev should be (1-1+2)%2=0, dp[1] = dp[0] + 1 = 5
Final result: 5
*/
The elegance of this approach lies in the mathematical relationship: if we want consecutive elements to sum to a target value modulo k, we can work backwards from the current element to determine what the previous element's remainder should be, enabling us to extend existing subsequences optimally.
Subscribe to my newsletter
Read articles from Anni Huang directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Anni Huang
Anni Huang
I am Anni HUANG, a software engineer with 3 years of experience in IDE development and Chatbot.