Skip to content

Commit 32cdf41

Browse files
authored
Merge pull request #3978 from Deynn0/feature/maximum-length-of-pair-chain
feat(dsa): add Maximum Length of Pair Chain medium DP problem
2 parents e01ef85 + 565905f commit 32cdf41

2 files changed

Lines changed: 292 additions & 2 deletions

File tree

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
---
2+
id: maximum-length-of-pair-chain
3+
title: Maximum Length of Pair Chain
4+
sidebar_label: Maximum Length of Pair Chain
5+
tags:
6+
- DSA
7+
- leetcode
8+
- dynamic-programming
9+
- array
10+
- greedy
11+
- memoization
12+
companies:
13+
- Google
14+
- Amazon
15+
description: "Solve the Maximum Length of Pair Chain problem using Dynamic Programming with memoization (LIS variant) and greedy interval scheduling."
16+
---
17+
18+
## Description:
19+
20+
You are given an array of `n` pairs `pairs` where `pairs[i] = [left_i, right_i]` and `left_i < right_i`.
21+
22+
A pair `p2 = [c, d]` follows a pair `p1 = [a, b]` if `b < c`. A chain of pairs can be formed in this fashion.
23+
24+
Return the *length longest chain which can be formed*.
25+
26+
You do not need to use up all the given intervals. You can select pairs in any order.
27+
28+
### Examples:
29+
30+
**Example 1:**
31+
32+
```text
33+
Input: pairs = [[1,2],[2,3],[3,4]]
34+
Output: 2
35+
Explanation: The longest chain is [1,2] -> [3,4].
36+
```
37+
38+
**Example 2:**
39+
40+
```text
41+
Input: pairs = [[1,2],[7,8],[4,5]]
42+
Output: 3
43+
Explanation: The longest chain is [1,2] -> [4,5] -> [7,8].
44+
```
45+
46+
### Constraints:
47+
48+
- `n == pairs.length`
49+
- `1 <= n <= 1000`
50+
- `-1000 <= left_i < right_i <= 1000`
51+
52+
---
53+
54+
## Video Explanation:
55+
56+
<LiteYouTubeEmbed
57+
id="DlgGx8GRo9M"
58+
params="autoplay=1&autohide=1&showinfo=0&rel=0"
59+
title="Maximum Length of Pair Chain"
60+
poster="maxresdefault"
61+
webp
62+
/>
63+
64+
---
65+
66+
## Approaches:
67+
68+
### 1. Dynamic Programming (Recursion with Memoization - LIS Variant)
69+
70+
#### Intuition:
71+
This problem can be framed as a variation of the classic **Longest Increasing Subsequence (LIS)** problem.
72+
Since we can select pairs in any order, we should first sort the pairs in ascending order based on their first element (`pairs[i][0]`).
73+
74+
Once sorted:
75+
- For each pair at index `indx`, we have two decisions:
76+
1. **Take the pair**: We can only take the pair if it is the first pair we pick (`prevI == -1`) or if its start coordinate is strictly greater than the end coordinate of the previously chosen pair (`pairs[indx][0] > pairs[prevI][1]`). If taken, the chain length increases by `1`, and the new previous index becomes `indx`.
77+
2. **Do not take the pair**: We skip the current pair and advance to the next index without modifying `prevI`.
78+
- The answer for the current state is the maximum of the two choices.
79+
- To prevent recomputing overlapping subproblems, we use a 2D memoization table `dp[indx][prevI + 1]`. The `+1` shift handles the base case when `prevI == -1`.
80+
81+
#### Complexity:
82+
- **Time Complexity:** $O(n^2)$ where $n$ is the number of pairs. There are $n \times (n+1)$ states, and each transition takes $O(1)$ time. Sorting takes $O(n \log n)$.
83+
- **Space Complexity:** $O(n^2)$ for the 2D DP memoization table and $O(n)$ recursion stack space.
84+
85+
---
86+
87+
### 2. Greedy Approach (Optimal)
88+
89+
#### Intuition:
90+
We can also view this problem as an **Interval Scheduling Problem**.
91+
To maximize the number of non-overlapping intervals (pairs), we should always choose the pair that ends earliest, leaving the maximum possible room for subsequent pairs.
92+
1. Sort `pairs` in ascending order by their second element (`pairs[i][1]`).
93+
2. Maintain `curr_end` initialized to negative infinity.
94+
3. For each pair `[start, end]`, if `start > curr_end`, increment the chain count and update `curr_end = end`.
95+
96+
#### Complexity:
97+
- **Time Complexity:** $O(n \log n)$ due to sorting the pairs.
98+
- **Space Complexity:** $O(1)$ auxiliary space (or $O(n)$ depending on the sorting implementation).
99+
100+
---
101+
102+
## Solutions
103+
104+
<Tabs groupId="programming-language">
105+
<TabItem value="cpp" label="C++" default>
106+
107+
```cpp
108+
#include <vector>
109+
#include <algorithm>
110+
111+
using namespace std;
112+
113+
class Solution {
114+
public:
115+
int rec(vector<vector<int>>& pairs, int indx, int prevI,
116+
vector<vector<int>>& dp) {
117+
if (indx == pairs.size()) {
118+
return 0;
119+
}
120+
121+
if (dp[indx][prevI + 1] != -1) {
122+
return dp[indx][prevI + 1];
123+
}
124+
125+
int take = 0;
126+
if (prevI == -1 || pairs[indx][0] > pairs[prevI][1]) {
127+
take = 1 + rec(pairs, indx + 1, indx, dp);
128+
}
129+
130+
int notake = rec(pairs, indx + 1, prevI, dp);
131+
132+
return dp[indx][prevI + 1] = max(take, notake);
133+
}
134+
135+
int findLongestChain(vector<vector<int>>& pairs) {
136+
int n = pairs.size();
137+
vector<vector<int>> dp(n, vector<int>(n + 1, -1));
138+
139+
sort(pairs.begin(), pairs.end(),
140+
[](const vector<int>& a, const vector<int>& b) {
141+
return a[0] < b[0];
142+
});
143+
144+
return rec(pairs, 0, -1, dp);
145+
}
146+
};
147+
```
148+
149+
</TabItem>
150+
<TabItem value="java" label="Java">
151+
152+
```java
153+
import java.util.Arrays;
154+
155+
class Solution {
156+
public int rec(int[][] pairs, int indx, int prevI, int[][] dp) {
157+
if (indx == pairs.length) {
158+
return 0;
159+
}
160+
161+
if (dp[indx][prevI + 1] != -1) {
162+
return dp[indx][prevI + 1];
163+
}
164+
165+
int take = 0;
166+
if (prevI == -1 || pairs[indx][0] > pairs[prevI][1]) {
167+
take = 1 + rec(pairs, indx + 1, indx, dp);
168+
}
169+
170+
int notake = rec(pairs, indx + 1, prevI, dp);
171+
172+
return dp[indx][prevI + 1] = Math.max(take, notake);
173+
}
174+
175+
public int findLongestChain(int[][] pairs) {
176+
int n = pairs.length;
177+
int[][] dp = new int[n][n + 1];
178+
for (int[] row : dp) {
179+
Arrays.fill(row, -1);
180+
}
181+
182+
Arrays.sort(pairs, (a, b) -> a[0] - b[0]);
183+
return rec(pairs, 0, -1, dp);
184+
}
185+
}
186+
```
187+
188+
</TabItem>
189+
<TabItem value="python" label="Python">
190+
191+
```python
192+
from typing import List
193+
194+
class Solution:
195+
def rec(self, pairs: List[List[int]], indx: int, prevI: int, dp: List[List[int]]) -> int:
196+
if indx == len(pairs):
197+
return 0
198+
199+
if dp[indx][prevI + 1] != -1:
200+
return dp[indx][prevI + 1]
201+
202+
take = 0
203+
if prevI == -1 or pairs[indx][0] > pairs[prevI][1]:
204+
take = 1 + self.rec(pairs, indx + 1, indx, dp)
205+
206+
notake = self.rec(pairs, indx + 1, prevI, dp)
207+
208+
dp[indx][prevI + 1] = max(take, notake)
209+
return dp[indx][prevI + 1]
210+
211+
def findLongestChain(self, pairs: List[List[int]]) -> int:
212+
n = len(pairs)
213+
dp = [[-1] * (n + 1) for _ in range(n)]
214+
pairs.sort(key=lambda x: x[0])
215+
return self.rec(pairs, 0, -1, dp)
216+
```
217+
218+
</TabItem>
219+
<TabItem value="javascript" label="JavaScript">
220+
221+
```javascript
222+
/**
223+
* Dynamic Programming (Memoization)
224+
* @param {number[][]} pairs
225+
* @return {number}
226+
*/
227+
var findLongestChain = function(pairs) {
228+
const n = pairs.length;
229+
const dp = Array.from({ length: n }, () => Array(n + 1).fill(-1));
230+
231+
pairs.sort((a, b) => a[0] - b[0]);
232+
233+
function rec(indx, prevI) {
234+
if (indx === n) return 0;
235+
if (dp[indx][prevI + 1] !== -1) return dp[indx][prevI + 1];
236+
237+
let take = 0;
238+
if (prevI === -1 || pairs[indx][0] > pairs[prevI][1]) {
239+
take = 1 + rec(indx + 1, indx);
240+
}
241+
242+
const notake = rec(indx + 1, prevI);
243+
244+
return (dp[indx][prevI + 1] = Math.max(take, notake));
245+
}
246+
247+
return rec(0, -1);
248+
};
249+
```
250+
251+
</TabItem>
252+
</Tabs>

‎src/data/generated/dsaProblemsIndex.json‎

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"generatedAt": "2026-08-22T03:25:28.521Z",
3-
"count": 90,
2+
"generatedAt": "2026-09-08T06:33:08.619Z",
3+
"count": 91,
44
"difficulties": [
55
"Easy",
66
"Medium",
@@ -155,6 +155,10 @@
155155
"value": "matrix",
156156
"label": "Matrix"
157157
},
158+
{
159+
"value": "memoization",
160+
"label": "Memoization"
161+
},
158162
{
159163
"value": "monotonic-queue",
160164
"label": "Monotonic Queue"
@@ -861,6 +865,23 @@
861865
"companies": [],
862866
"url": "/docs/dsa-problems/medium/maximum-ice-cream-bars"
863867
},
868+
{
869+
"id": "maximum-length-of-pair-chain",
870+
"title": "Maximum Length of Pair Chain",
871+
"description": "Solve the Maximum Length of Pair Chain problem using Dynamic Programming with memoization (LIS variant) and greedy interval scheduling.",
872+
"difficulty": "Medium",
873+
"tags": [
874+
"dynamic-programming",
875+
"array",
876+
"greedy",
877+
"memoization"
878+
],
879+
"companies": [
880+
"Google",
881+
"Amazon"
882+
],
883+
"url": "/docs/dsa-problems/medium/maximum-length-of-pair-chain"
884+
},
864885
{
865886
"id": "maximum-number-of-balloons",
866887
"title": "Maximum Number of Balloons",
@@ -2142,6 +2163,23 @@
21422163
"companies": [],
21432164
"url": "/docs/dsa-problems/medium/maximum-ice-cream-bars"
21442165
},
2166+
"maximum-length-of-pair-chain": {
2167+
"id": "maximum-length-of-pair-chain",
2168+
"title": "Maximum Length of Pair Chain",
2169+
"description": "Solve the Maximum Length of Pair Chain problem using Dynamic Programming with memoization (LIS variant) and greedy interval scheduling.",
2170+
"difficulty": "Medium",
2171+
"tags": [
2172+
"dynamic-programming",
2173+
"array",
2174+
"greedy",
2175+
"memoization"
2176+
],
2177+
"companies": [
2178+
"Google",
2179+
"Amazon"
2180+
],
2181+
"url": "/docs/dsa-problems/medium/maximum-length-of-pair-chain"
2182+
},
21452183
"maximum-number-of-balloons": {
21462184
"id": "maximum-number-of-balloons",
21472185
"title": "Maximum Number of Balloons",

0 commit comments

Comments
 (0)