Given two strings s and t, return the number of distinct subsequences of s which equals t.
A string's subsequence is a new string formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (i.e., "ACE" is a subsequence of "ABCDE" while "AEC" is not).
The test cases are generated so that the answer fits on a 32-bit signed integer.
Input: s = "rabbbit", t = "rabbit"
Output: 3
Explanation: There are 3 ways to generate "rabbit" from "rabbbit":
Input: s = "babgbag", t = "bag"
Output: 5
Explanation: There are 5 ways to generate "bag" from "babgbag":
1 <= s.length, t.length <= 1000This problem is a classic 2D Dynamic Programming problem involving string matching and counting subsequences.
The key insight is to build up the count of distinct subsequences by making a choice at each character:
s - inherit the count from the previous rows (if it matches) - add the count from the diagonaldp[i][j] = number of distinct subsequences of s[0...i-1] that equal t[0...j-1]
dp[i][0] = 1 for all i: An empty target string can be formed in exactly 1 way (by selecting nothing)dp[0][j] = 0 for all j > 0: An empty source cannot form a non-empty targetFor each position (i, j):
Always inherit from the previous row (don't use current character):
dp[i][j] = dp[i-1][j]
If characters match s[i-1] == t[j-1], add the diagonal count (use current character):
dp[i][j] += dp[i-1][j-1]
(m+1) × (n+1) where m = len(s), n = len(t)dp[i][0] = 1 for all idp[i][j] = dp[i-1][j] (skip current character in s)s[i-1] == t[j-1], add dp[i-1][j-1] (use current character)dp[m][n]The space can be optimized to O(n) by using a 1D array and processing from right to left:
def numDistinct(s: str, t: str) -> int:
n = len(t)
dp = [0] * (n + 1)
dp[0] = 1
for char_s in s:
for j in range(n, 0, -1): # Right to left
if char_s == t[j - 1]:
dp[j] += dp[j - 1]
return dp[n]
Processing right-to-left ensures we don't overwrite values we still need.
dp[i][j] = dp[i-1][j]dp[i][0] = 1 (empty target has 1 way)s[i-1] for row i)1class Solution {
2 // Approach 1: 2D DP (Bottom-Up)
3 // Time: O(m * n), Space: O(m * n)
4 public int numDistinct(String s, String t) {
5 int m = s.length();
6 int n = t.length();
7
8 // dp[i][j] = number of distinct subsequences of s[0...i-1] that equal t[0...j-1]
9 long[][] dp = new long[m + 1][n + 1];
10
11 // Base case: empty target can be formed in 1 way
12 for (int i = 0; i <= m; i++) {
13 dp[i][0] = 1;
14 }
15
16 // Fill the DP table
17 for (int i = 1; i <= m; i++) {
18 for (int j = 1; j <= n; j++) {
19 // Don't use current character from s
20 dp[i][j] = dp[i - 1][j];
21
22 // If characters match, add count from diagonal
23 if (s.charAt(i - 1) == t.charAt(j - 1)) {
24 dp[i][j] += dp[i - 1][j - 1];
25 }
26 }
27 }
28
29 return (int) dp[m][n];
30 }
31
32 // Approach 2: Space-Optimized 1D DP
33 // Time: O(m * n), Space: O(n)
34 public int numDistinctOptimized(String s, String t) {
35 int m = s.length();
36 int n = t.length();
37
38 long[] dp = new long[n + 1];
39 dp[0] = 1; // Base case
40
41 for (int i = 1; i <= m; i++) {
42 // Process from right to left to avoid overwriting needed values
43 for (int j = n; j >= 1; j--) {
44 if (s.charAt(i - 1) == t.charAt(j - 1)) {
45 dp[j] += dp[j - 1];
46 }
47 }
48 }
49
50 return (int) dp[n];
51 }
52
53 // Approach 3: Memoization (Top-Down DP)
54 // Time: O(m * n), Space: O(m * n)
55 public int numDistinctMemo(String s, String t) {
56 Long[][] memo = new Long[s.length()][t.length()];
57 return (int) dfs(s, t, 0, 0, memo);
58 }
59
60 private long dfs(String s, String t, int i, int j, Long[][] memo) {
61 // If we've matched all of t, found 1 subsequence
62 if (j == t.length()) {
63 return 1;
64 }
65
66 // If we've exhausted s but not t, no match
67 if (i == s.length()) {
68 return 0;
69 }
70
71 // Check memo
72 if (memo[i][j] != null) {
73 return memo[i][j];
74 }
75
76 long count = 0;
77
78 // Don't use current character from s
79 count = dfs(s, t, i + 1, j, memo);
80
81 // If characters match, try using current character
82 if (s.charAt(i) == t.charAt(j)) {
83 count += dfs(s, t, i + 1, j + 1, memo);
84 }
85
86 memo[i][j] = count;
87 return count;
88 }
89}
90