115. Distinct Subsequences #
Problem #
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 remaining characters’ relative positions. (i.e., "ACE" is a subsequence of "ABCDE" while "AEC" is not).
It is guaranteed the answer fits on a 32-bit signed integer.
Example 1:
Input: s = "rabbbit", t = "rabbit"
Output: 3
Explanation:
As shown below, there are 3 ways you can generate "rabbit" from S.
rabbbitrabbbitrabbbit
Example 2:
Input: s = "babgbag", t = "bag"
Output: 5
Explanation:
As shown below, there are 5 ways you can generate "bag" from S.
babgbagbabgbagbabgbagbabgbagbabgbag
Constraints:
0 <= s.length, t.length <= 1000sandtconsist of English letters.
Problem Summary #
Given a string s and a string t, calculate the number of times t appears among the subsequences of s. A subsequence of a string is a new string formed by deleting some characters (or none) without disturbing the relative positions of the remaining characters. (For example, “ACE” is a subsequence of “ABCDE”, while “AEC” is not.) The problem data guarantees that the answer fits within the range of a 32-bit signed integer.
Solution Approach #
How many strings
tcan be contained in the stringsat most. This contains many overlapping subproblems, so try to solve this problem with dynamic programming. Definedp[i][j]as the number of timest[j:]appears among the subsequences ofs[i:]. For initialization, first consider the boundary conditions. Wheni = len(s)and0≤ j < len(t),s[i:]is an empty string andt[j:]is not empty, sodp[len(s)][j] = 0. Whenj = len(t)and0 ≤ i < len(s),t[j:]is an empty string, and the empty string is a subsequence of any string. Sodp[i][n] = 1.When
i < len(s)andj < len(t), ifs[i] == t[j], there are 2 matching methods. The first is to matchs[i]witht[j], thent[j+1:]matches a subsequence ofs[i+1:], and the number of subsequences isdp[i+1][j+1]; the second is not to matchs[i]witht[j], andt[j:]is treated as a subsequence ofs[i+1:], with the number of subsequences beingdp[i+1][j]. Combining the 2 cases, whens[i] == t[j],dp[i][j] = dp[i+1][j+1] + dp[i+1][j].If
\[ dp[i][j] = \left\{\begin{matrix}dp[i+1][j+1]+dp[i+1][j]&,s[i]=t[j]\\ dp[i+1][j]&,s[i]!=t[j]\end{matrix}\right. \]s[i] != t[j], thent[j:]can only be a subsequence ofs[i+1:], and the number of subsequences isdp[i+1][j]. So whens[i] != t[j],dp[i][j] = dp[i+1][j]. In summary, we get:Finally, the optimized version. After writing the above code, you can see that the table-filling process goes from the bottom-right corner all the way to the top-left corner. The table-filling order is row by row from bottom to top. Within each row, fill from right to left. Therefore, this two-dimensional data can be compressed into one dimension. Because filling the current row only needs information from the next row, and more specifically, it uses information from the elements to the right in the next row. Therefore, each time this row is updated, first store the old value, and when calculating the update for this row, update from right to left. Doing this can reduce one dimension of space and compress the original two-dimensional array into a one-dimensional array.
Code #
package leetcode
// Solution 1: Compressed DP
func numDistinct(s string, t string) int {
dp := make([]int, len(s)+1)
for i, curT := range t {
pre := 0
for j, curS := range s {
if i == 0 {
pre = 1
}
newDP := dp[j+1]
if curT == curS {
dp[j+1] = dp[j] + pre
} else {
dp[j+1] = dp[j]
}
pre = newDP
}
}
return dp[len(s)]
}
// Solution 2: Standard DP
func numDistinct1(s, t string) int {
m, n := len(s), len(t)
if m < n {
return 0
}
dp := make([][]int, m+1)
for i := range dp {
dp[i] = make([]int, n+1)
dp[i][n] = 1
}
for i := m - 1; i >= 0; i-- {
for j := n - 1; j >= 0; j-- {
if s[i] == t[j] {
dp[i][j] = dp[i+1][j+1] + dp[i+1][j]
} else {
dp[i][j] = dp[i+1][j]
}
}
}
return dp[0][0]
}