class Solution:
def numDistinct(self, s: str, t: str) -> int:
m, n = len(s), len(t)
# dp[i][j]: number of subsequences of s[0:i] equals t[0:j]
dp = [[0] * (n + 1) for _ in range(m + 1)]
# An empty t can be formed by deleting all chars from s
for i in range(m + 1):
dp[i][0] = 1
for i in range(1, m + 1):
for j in range(1, n + 1):
if s[i - 1] == t[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]
else:
dp[i][j] = dp[i - 1][j]
return dp[m][n]
class Solution {
public:
int numDistinct(string s, string t) {
int m = s.size(), n = t.size();
vector<vector<long long>> dp(m + 1, vector<long long>(n + 1, 0));
for (int i = 0; i <= m; ++i) dp[i][0] = 1;
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (s[i - 1] == t[j - 1])
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
else
dp[i][j] = dp[i - 1][j];
}
}
return dp[m][n];
}
};
class Solution {
public int numDistinct(String s, String t) {
int m = s.length(), n = t.length();
long[][] dp = new long[m + 1][n + 1];
for (int i = 0; i <= m; i++) dp[i][0] = 1;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (s.charAt(i - 1) == t.charAt(j - 1))
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
else
dp[i][j] = dp[i - 1][j];
}
}
return (int)dp[m][n];
}
}
var numDistinct = function(s, t) {
const m = s.length, n = t.length;
const dp = Array.from({length: m + 1}, () => Array(n + 1).fill(0));
for (let i = 0; i <= m; i++) dp[i][0] = 1;
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (s[i - 1] === t[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[m][n];
};
Given two strings, s
and t
, determine how many distinct subsequences of s
equal t
. A subsequence is a sequence that can be derived from another string by deleting some (or no) characters, without changing the order of the remaining characters.
t
from s
using this rule.s
can be used at most once for each subsequence.
Example:
s = "rabbbit"
, t = "rabbit"
→ Output: 3
At first glance, you might consider generating every possible subsequence of s
and checking which ones match t
. However, since the number of subsequences grows exponentially with the length of s
, this brute-force method is not practical for large inputs.
Instead, let's look for a smarter way. Notice that for each character in s
, we can either use it (if it matches the current character in t
) or skip it. This suggests a recursive or dynamic programming approach, where we break the problem into smaller subproblems: how many ways can we form the first j
characters of t
from the first i
characters of s
?
By thinking recursively and caching results (dynamic programming), we avoid recalculating the same subproblems and make our solution efficient.
We use dynamic programming to solve this problem efficiently. Let's break down the steps:
dp[i][j]
be the number of distinct subsequences of s[0..i-1]
that equals t[0..j-1]
.t
is empty (j == 0
), there is exactly one subsequence (the empty subsequence) for any prefix of s
: dp[i][0] = 1
.s
is empty (i == 0
) and t
is not, there are zero ways to form t
: dp[0][j] = 0
for j > 0
.s[i-1] == t[j-1]
, we have two choices:
s[i-1]
to match t[j-1]
: dp[i-1][j-1]
s[i-1]
: dp[i-1][j]
dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
s[i-1] != t[j-1]
, we can only skip s[i-1]
: dp[i][j] = dp[i-1][j]
i
from 1 to m
(len(s)
), and j
from 1 to n
(len(t)
), filling the table using the rules above.dp[m][n]
: the number of ways to form all of t
from all of s
.This approach is efficient because it avoids redundant calculations and only considers valid subsequences.
Let's walk through the example: s = "rabbbit"
, t = "rabbit"
.
8 x 7
(since s
has length 7, t
has length 6, and we add 1 for the empty string).dp[i][0] = 1
for all i
(empty t
case).s
and t
, apply the recurrence:s[i-1] == t[j-1]
(e.g., the 'b's at positions 2, 3, 4 in s
and position 2 in t
), we add both choices.dp[7][6]
(bottom-right cell) will be 3
, meaning there are 3 distinct ways to form "rabbit" from "rabbbit".s
as the 'b' in t
(with the other two skipped each time).s
, which is 2^m
(where m
is the length of s
).t
, so the overall time is exponential: O(2^m \cdot n)
.(m+1) x (n+1)
, so time and space complexity are both O(mn)
.O(n)
by only keeping two rows.The Distinct Subsequences problem asks us to count how many ways we can form one string as a subsequence of another. While a brute-force approach is infeasible, a dynamic programming solution allows us to build up the answer efficiently by considering subproblems and using optimal substructure. The key insight is recognizing when to use or skip characters, and how to translate that into a recurrence relation. By leveraging a DP table, we solve the problem in polynomial time, making it practical even for large strings.