Skip to content

Commit 68d4213

Browse files
feat: add NeetCode 150 - 2D Dynamic Programming category (8 problems)
Complete implementations with multiple approaches: - Unique Paths (Grid traversal) - Longest Common Subsequence (String comparison) - Edit Distance (String transformation) - Interleaving String (String combination) - Distinct Subsequences (Subsequence counting) - Word Break II (Sentence generation) - Palindrome Partitioning (Palindrome detection) - Regular Expression Matching (Pattern matching) All solutions include DP, space-optimized DP, and alternative approaches
1 parent 61da6c2 commit 68d4213

File tree

16 files changed

+2139
-0
lines changed

16 files changed

+2139
-0
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Distinct Subsequences
2+
3+
## Problem Statement
4+
5+
Given two strings `s` and `t`, return the number of distinct subsequences of `s` which equals `t`.
6+
7+
The test cases are generated so that the answer fits in a 32-bit signed integer.
8+
9+
## Examples
10+
11+
**Example 1:**
12+
```
13+
Input: s = "rabbbit", t = "rabbit"
14+
Output: 3
15+
Explanation:
16+
As shown below, there are 3 ways you can generate "rabbit" from s.
17+
rabbbit
18+
rabbbit
19+
rabbbit
20+
```
21+
22+
## Approach
23+
24+
### Method 1: Dynamic Programming (Recommended)
25+
1. Use DP table to store number of distinct subsequences
26+
2. dp[i][j] = number of ways to form t[0:j] from s[0:i]
27+
3. Handle character matching and skipping
28+
4. Most efficient approach
29+
30+
**Time Complexity:** O(m * n) - Fill DP table
31+
**Space Complexity:** O(m * n) - DP table
32+
33+
### Method 2: Space-Optimized DP
34+
1. Use only two rows instead of full table
35+
2. Alternate between current and previous row
36+
3. Most memory efficient
37+
38+
**Time Complexity:** O(m * n) - Fill DP table
39+
**Space Complexity:** O(min(m, n)) - Two rows
40+
41+
## Algorithm
42+
43+
```
44+
1. Initialize dp[0][j] = 1 for all j (empty string)
45+
2. For i from 1 to m:
46+
For j from 1 to n:
47+
If s[i-1] == t[j-1]:
48+
dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
49+
Else:
50+
dp[i][j] = dp[i-1][j]
51+
3. Return dp[m][n]
52+
```
53+
54+
## Key Insights
55+
56+
- **State Transition**: dp[i][j] = dp[i-1][j-1] + dp[i-1][j] if match, else dp[i-1][j]
57+
- **Base Case**: dp[0][j] = 1 (empty string can form empty target)
58+
- **Character Matching**: Check if characters match
59+
- **Space Optimization**: Use only two rows
60+
61+
## Alternative Approaches
62+
63+
1. **Recursion**: Use recursive approach with memoization
64+
2. **Backtracking**: Use backtracking to explore all possibilities
65+
3. **Iterative**: Use iterative approach
66+
67+
## Edge Cases
68+
69+
- Empty strings: Return 1
70+
- No subsequence: Return 0
71+
- Single character: Handle appropriately
72+
- Identical strings: Return 1
73+
74+
## Applications
75+
76+
- Dynamic programming patterns
77+
- String algorithms
78+
- Algorithm design patterns
79+
- Interview preparation
80+
- System design
81+
82+
## Optimization Opportunities
83+
84+
- **Space Optimization**: O(min(m, n)) space complexity
85+
- **Single Pass**: O(m * n) time complexity
86+
- **Memory Efficient**: Use only two rows
87+
- **No Recursion**: Avoid stack overflow
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/**
2+
* Time Complexity: O(m * n) - Fill DP table
3+
* Space Complexity: O(m * n) - DP table
4+
*/
5+
class Solution {
6+
public int numDistinct(String s, String t) {
7+
int m = s.length();
8+
int n = t.length();
9+
int[][] dp = new int[m + 1][n + 1];
10+
11+
// Initialize base case: empty string can form empty target
12+
for (int i = 0; i <= m; i++) {
13+
dp[i][0] = 1;
14+
}
15+
16+
// Fill DP table
17+
for (int i = 1; i <= m; i++) {
18+
for (int j = 1; j <= n; j++) {
19+
if (s.charAt(i - 1) == t.charAt(j - 1)) {
20+
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
21+
} else {
22+
dp[i][j] = dp[i - 1][j];
23+
}
24+
}
25+
}
26+
27+
return dp[m][n];
28+
}
29+
}
30+
31+
// Alternative approach using space-optimized DP
32+
class SolutionSpaceOptimized {
33+
public int numDistinct(String s, String t) {
34+
int m = s.length();
35+
int n = t.length();
36+
37+
// Use shorter string for space optimization
38+
if (m < n) {
39+
return numDistinct(t, s);
40+
}
41+
42+
int[] prev = new int[n + 1];
43+
int[] curr = new int[n + 1];
44+
45+
// Initialize first row
46+
prev[0] = 1;
47+
48+
for (int i = 1; i <= m; i++) {
49+
curr[0] = 1;
50+
for (int j = 1; j <= n; j++) {
51+
if (s.charAt(i - 1) == t.charAt(j - 1)) {
52+
curr[j] = prev[j - 1] + prev[j];
53+
} else {
54+
curr[j] = prev[j];
55+
}
56+
}
57+
int[] temp = prev;
58+
prev = curr;
59+
curr = temp;
60+
}
61+
62+
return prev[n];
63+
}
64+
}
65+
66+
// Alternative approach using recursion with memoization
67+
class SolutionMemoization {
68+
public int numDistinct(String s, String t) {
69+
int m = s.length();
70+
int n = t.length();
71+
int[][] memo = new int[m][n];
72+
73+
for (int[] row : memo) {
74+
Arrays.fill(row, -1);
75+
}
76+
77+
return numDistinctHelper(s, t, 0, 0, memo);
78+
}
79+
80+
private int numDistinctHelper(String s, String t, int i, int j, int[][] memo) {
81+
if (j == t.length()) {
82+
return 1;
83+
}
84+
85+
if (i == s.length()) {
86+
return 0;
87+
}
88+
89+
if (memo[i][j] != -1) {
90+
return memo[i][j];
91+
}
92+
93+
int result = 0;
94+
95+
if (s.charAt(i) == t.charAt(j)) {
96+
result += numDistinctHelper(s, t, i + 1, j + 1, memo);
97+
}
98+
99+
result += numDistinctHelper(s, t, i + 1, j, memo);
100+
101+
memo[i][j] = result;
102+
return result;
103+
}
104+
}
105+
106+
// Alternative approach using iterative
107+
class SolutionIterative {
108+
public int numDistinct(String s, String t) {
109+
int m = s.length();
110+
int n = t.length();
111+
int[][] dp = new int[m + 1][n + 1];
112+
113+
for (int i = 0; i <= m; i++) {
114+
dp[i][0] = 1;
115+
}
116+
117+
for (int i = 1; i <= m; i++) {
118+
for (int j = 1; j <= n; j++) {
119+
if (s.charAt(i - 1) == t.charAt(j - 1)) {
120+
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
121+
} else {
122+
dp[i][j] = dp[i - 1][j];
123+
}
124+
}
125+
}
126+
127+
return dp[m][n];
128+
}
129+
}
130+
131+
// Alternative approach using bottom-up DP
132+
class SolutionBottomUp {
133+
public int numDistinct(String s, String t) {
134+
int m = s.length();
135+
int n = t.length();
136+
int[][] dp = new int[m + 1][n + 1];
137+
138+
for (int j = 0; j <= n; j++) {
139+
dp[m][j] = (j == n) ? 1 : 0;
140+
}
141+
142+
for (int i = m - 1; i >= 0; i--) {
143+
for (int j = n - 1; j >= 0; j--) {
144+
if (s.charAt(i) == t.charAt(j)) {
145+
dp[i][j] = dp[i + 1][j + 1] + dp[i + 1][j];
146+
} else {
147+
dp[i][j] = dp[i + 1][j];
148+
}
149+
}
150+
}
151+
152+
return dp[0][0];
153+
}
154+
}
155+
156+
// Alternative approach using recursion
157+
class SolutionRecursion {
158+
public int numDistinct(String s, String t) {
159+
return numDistinctHelper(s, t, 0, 0);
160+
}
161+
162+
private int numDistinctHelper(String s, String t, int i, int j) {
163+
if (j == t.length()) {
164+
return 1;
165+
}
166+
167+
if (i == s.length()) {
168+
return 0;
169+
}
170+
171+
int result = 0;
172+
173+
if (s.charAt(i) == t.charAt(j)) {
174+
result += numDistinctHelper(s, t, i + 1, j + 1);
175+
}
176+
177+
result += numDistinctHelper(s, t, i + 1, j);
178+
179+
return result;
180+
}
181+
}
182+
183+
// More concise version
184+
class SolutionConcise {
185+
public int numDistinct(String s, String t) {
186+
int m = s.length(), n = t.length();
187+
int[][] dp = new int[m + 1][n + 1];
188+
189+
for (int i = 0; i <= m; i++) dp[i][0] = 1;
190+
191+
for (int i = 1; i <= m; i++) {
192+
for (int j = 1; j <= n; j++) {
193+
dp[i][j] = s.charAt(i - 1) == t.charAt(j - 1)
194+
? dp[i - 1][j - 1] + dp[i - 1][j]
195+
: dp[i - 1][j];
196+
}
197+
}
198+
199+
return dp[m][n];
200+
}
201+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Edit Distance
2+
3+
## Problem Statement
4+
5+
Given two strings `word1` and `word2`, return the minimum number of operations required to convert `word1` to `word2`.
6+
7+
You have the following three operations permitted on a word:
8+
- Insert a character
9+
- Delete a character
10+
- Replace a character
11+
12+
## Examples
13+
14+
**Example 1:**
15+
```
16+
Input: word1 = "horse", word2 = "ros"
17+
Output: 3
18+
Explanation:
19+
horse -> rorse (replace 'h' with 'r')
20+
rorse -> rose (remove 'r')
21+
rose -> ros (remove 'e')
22+
```
23+
24+
## Approach
25+
26+
### Method 1: Dynamic Programming (Recommended)
27+
1. Use DP table to store minimum operations
28+
2. dp[i][j] = min operations to convert word1[0:i] to word2[0:j]
29+
3. Handle insert, delete, replace operations
30+
4. Most efficient approach
31+
32+
**Time Complexity:** O(m * n) - Fill DP table
33+
**Space Complexity:** O(m * n) - DP table
34+
35+
### Method 2: Space-Optimized DP
36+
1. Use only two rows instead of full table
37+
2. Alternate between current and previous row
38+
3. Most memory efficient
39+
40+
**Time Complexity:** O(m * n) - Fill DP table
41+
**Space Complexity:** O(min(m, n)) - Two rows
42+
43+
## Algorithm
44+
45+
```
46+
1. Initialize dp[0][j] = j and dp[i][0] = i
47+
2. For i from 1 to m:
48+
For j from 1 to n:
49+
If word1[i-1] == word2[j-1]:
50+
dp[i][j] = dp[i-1][j-1]
51+
Else:
52+
dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
53+
3. Return dp[m][n]
54+
```
55+
56+
## Key Insights
57+
58+
- **State Transition**: dp[i][j] = min operations to convert word1[0:i] to word2[0:j]
59+
- **Base Cases**: dp[0][j] = j, dp[i][0] = i
60+
- **Three Operations**: Insert, delete, replace
61+
- **Space Optimization**: Use only two rows
62+
63+
## Alternative Approaches
64+
65+
1. **Recursion**: Use recursive approach with memoization
66+
2. **Backtracking**: Use backtracking to find actual operations
67+
3. **Iterative**: Use iterative approach
68+
69+
## Edge Cases
70+
71+
- Empty strings: Return length of non-empty string
72+
- Identical strings: Return 0
73+
- Single character: Handle appropriately
74+
- No common characters: Return max length
75+
76+
## Applications
77+
78+
- Dynamic programming patterns
79+
- String algorithms
80+
- Algorithm design patterns
81+
- Interview preparation
82+
- System design
83+
84+
## Optimization Opportunities
85+
86+
- **Space Optimization**: O(min(m, n)) space complexity
87+
- **Single Pass**: O(m * n) time complexity
88+
- **Memory Efficient**: Use only two rows
89+
- **No Recursion**: Avoid stack overflow

0 commit comments

Comments
 (0)