@@ -18,8 +18,162 @@ Return `3`.
18
18
19
19
20
20
#### Analysis
21
+ 这题题意是要求在 S 中的所有包含 T 的不同子序列的数量。比如题例中就包含三个:"bbbit", "abbbit", "rabbbit",这三个子序列均包含了 T 。
21
22
23
+ 看到有关字符串的子序列或者配准类的问题,首先应该考虑的就是用动态规划 Dynamic Programming 来求解,而且,要考虑空字符串的情况,因为空串也是任意字符串的一个子序列。
24
+ DP 类型的题惯例的三步走:定义状态,推导递推公式,确定状态计算方向和起始状态。
25
+ 把 DP[ i] [ j ] 定义为 S[ 0: j-1 ] 中存在包含 T[ 0: i-1 ] 的 distinct subsequence 的个数。显然如果 j < i,DP[ i] [ j ] = 0。
22
26
27
+ 也可以这样想,设母串的长度为 j,子串的长度为 i,我们要求的就是长度为 i 的子串在长度为 j 的母串的所有子串中出现的次数,设为 dp[ i] [ j ] 。
23
28
29
+ 考虑下列两种情况:
24
30
31
+ - 若母串的最后一个字符与子串的最后一个字符不同(` S[j] != T[i] ` ),则长度为 i 的子串在长度为 j 的母串中出现的次数就是母串的前 j - 1 个字符中子串出现的次数,即 dp[ i] [ j ] = dp[ i] [ j - 1 ] ;
32
+ - 若母串的最后一个字符与子串的最后一个字符相同(` S[j] == T[i] ` ),那么最终数量是下列两种情况相加,也即 dp[ i] [ j ] = dp[ i] [ j - 1 ] + t[ i - 1] [ j - 1 ] 。
33
+ + 母串的前 j - 1 个字符也可能出现整个子串,T[ 0: i ]
34
+ + 母串的前 j - 1 个字符出现子串的前 i - 1个字符,T[ 0: i-1 ]
25
35
36
+ 转换方程:
37
+
38
+ DP[i][j] = DP[i][j-1], S[j-1] != T[i-1]
39
+ DP[i][j] = DP[i-1][j-1] + DP[i][j-1], S[j-1] == T[i-1]
40
+
41
+ 初始值:
42
+
43
+ 先设置第 0 行、第 0 列的值。
44
+
45
+ 第 0 列:DP[i][0] = 0,i > 0。 S 是空字符串,T 不是空字符串,显然 T 不可能成为 S 的 subsequence。
46
+ 第 0 行:DP[0][j] = 1,j >= 0。T 是空字符串,那么显然是 S 的 1 个 subsequence
47
+
48
+ ** 优化**
49
+ 由于只跟上一次结果有关,可以使用滚动数组来减小空间复杂度。
50
+
51
+ 对于二维数组的解集矩阵来说,实际上只有上三角(或者下三角)的数据是有效的,因此可以通过求矩阵上三角(或者下三角)的方式来缩减空间,比如:
52
+
53
+ — r a b b i t
54
+ - 1 0 0 0 0 0 0
55
+ r 1 1 0 0 0 0 0
56
+ a 1 1 1 0 0 0 0
57
+ b 1 1 1 1 0 0 0
58
+ b 1 1 1 2 1 0 0
59
+ b 1 1 1 3 3 0 0
60
+ i 1 1 1 3 3 3 0
61
+ t 1 1 1 3 3 3 3
62
+ 图(一)
63
+
64
+ 或者
65
+
66
+ — r a b b b i t
67
+ - 1 1 1 1 1 1 1 1
68
+ r 0 1 1 1 1 1 1 1
69
+ a 0 0 1 1 1 1 1 1
70
+ b 0 0 0 1 2 3 3 3
71
+ b 0 0 0 0 1 3 3 3
72
+ i 0 0 0 0 0 0 3 3
73
+ t 0 0 0 0 0 0 0 3
74
+ 图(二)
75
+
76
+
77
+
78
+ 知道了解集的结构之后,我们就可以使用一维数组,然后按照上图所示的方式来进行遍历。图一应该是从右往左遍历,图二则是从左往右遍历。
79
+ 因为当发生 S[ i] == T[ j] 的时候,dp[ j] = dp[ j] + dp[ j - 1] 。
80
+
81
+ ##### Solutions
82
+
83
+ 1 . DP With 2D Array
84
+
85
+ ``` cpp
86
+ class Solution {
87
+ public:
88
+ int numDistinct(string s, string t) {
89
+ int m = s.length(), n = t.length();
90
+ vector<vector<int >> dp(n + 1, vector<int >(m + 1, 0));
91
+
92
+ for (int i = 0; i <= m; ++i) dp[0][i] = 1;
93
+ for (int i = 1; i <= n; ++i) {
94
+ for (int j = 1; j <= m; ++j) {
95
+ if (s[j - 1] != t[i - 1]) dp[i][j] = dp[i][j - 1];
96
+ else {
97
+ dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1];
98
+ }
99
+ }
100
+ }
101
+ return dp[n][m];
102
+ }
103
+ };
104
+ ```
105
+
106
+ 正序
107
+
108
+ ``` cpp
109
+ class Solution {
110
+ public:
111
+ int numDistinct(string s, string t) {
112
+ int m = s.length(), n = t.length();
113
+ vector<vector<int >> dp(m + 1, vector<int >(n + 1, 0));
114
+ for (int i = 0; i <= m; ++i) dp[ i] [ 0 ] = 1;
115
+ for (int i = 1; i <= m; ++i) {
116
+ for (int j = 1; j <= n; ++j) {
117
+ if (s[ i - 1] != t[ j - 1] ) dp[ i] [ j ] = dp[ i - 1] [ j ] ;
118
+ else {
119
+ dp[ i] [ j ] = dp[ i - 1] [ j ] + dp[ i - 1] [ j - 1 ] ;
120
+ }
121
+ }
122
+ }
123
+ return dp[ m] [ n ] ;
124
+ }
125
+ };
126
+ ```
127
+
128
+ 2. DP With 1D Array
129
+ j 从尾到头,因为每次要使用上一次 loop 的值。如果从头往尾扫的话,重复计算了。
130
+
131
+ ```cpp
132
+ class Solution {
133
+ public:
134
+ int numDistinct(string s, string t) {
135
+ int m = s.length(), n = t.length();
136
+ if (m < n) return 0;
137
+ vector<int> dp(n + 1, 0);
138
+ dp[0] = 1; // 相当于 dp[0][0],肯定是 1
139
+ for (int i = 1; i <= m; ++i) {
140
+ for (int j = n; j >= 1; --j) { // 从后往前
141
+ if (s[i - 1] == t[j - 1]) {
142
+ dp[j] += dp[j - 1];
143
+ }
144
+ }
145
+ }
146
+ return dp[n];
147
+ }
148
+ };
149
+ ```
150
+
151
+ 3 . DP With 1D Array
152
+
153
+ ``` cpp
154
+ class Solution {
155
+ public:
156
+ int numDistinct(string s, string t) {
157
+ int n = s.size(), m = t.size();
158
+ vector<int > dp(n + 1, 1);
159
+
160
+ for(int i = 1; i <= m; i++) {
161
+ int upLeft = dp[ 0] ;
162
+ dp[ 0] = 0;
163
+ for(int j = 1; j <= n; j++) {
164
+ int temp = dp[ j] ;
165
+ dp[ j] = dp[ j - 1] ;
166
+ if(s[ j - 1] == t[ i - 1] )
167
+ dp[ j] += upLeft;
168
+ upLeft = temp;
169
+ }
170
+ }
171
+
172
+ return dp[ n] ;
173
+ }
174
+ };
175
+ ```
176
+
177
+ #### Reference
178
+
179
+ [LeetCode 115]:https://leetcode.com/problems/distinct-subsequences
0 commit comments