Week10
Dynamic Programming
question source: Distinct Subsequences
question description
Given a string S and a string T, count the number of distinct subsequences of S which equals T.
A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, “ACE” is a subsequence of “ABCDE” while “AEC” is not).
Example 1:
Input: S = “rabbbit”, T = “rabbit”
Output: 3
Explanation:
As shown below, there are 3 ways you can generate “rabbit” from S.
(The caret symbol ^ means the chosen letters)
rabbbit
^^^^ ^^
rabbbit
^^ ^^^^
rabbbit
^^^ ^^^
Example 2:
Input: S = “babgbag”, T = “bag”
Output: 5
Explanation:
As shown below, there are 5 ways you can generate “bag” from S.
(The caret symbol ^ means the chosen letters)
babgbag
^^ ^
babgbag
^^ ^
babgbag
^ ^^
babgbag
^ ^^
babgbag
^^^
这是一道标签为动态规划的题。这周讲动态规划的习题课。这类的题目,说简单不简单,两节课才讲了6道题,说难也不难,想出来之后才恍然大悟,实现起来也的确很简单。但关键就是怎么想出来,怎么分析题目,并用动态规划的思想去解决。动态规划是一种非常强大的计算模式,其解决问题的方式是首先定义它的一组子问题,然后按照由小到大,以小问题的解答支持大问题求解的模式,依次解决所有子问题,并最终得到原问题(最大的子问题)的求解。 这题标签为hard,对于这学期才接触动态规划算法的我来说,的确挺难。
解决方法
这题是说给出两个字符串S和T,给出S中有多少个子序列与T相等。这跟我们上课所说的编辑距离的经典例题有点像,很自然地,就想到用二维数组来存放子问题的解。
我们要用一种途径来缩小原来的问题。
现定义
f
[
i
]
[
j
]
=
S
[
0
:
i
]
中
子
序
列
为
T
[
0
:
j
]
的
个
数
f[i][j] = S[0:i]中子序列为T[0:j]的个数
f[i][j]=S[0:i]中子序列为T[0:j]的个数
那么问题的解就是 f[s.size] [t.size] ;
好了,现在就得思考子问题了,f[i][j]到底可不可以通过f[i - 1][j], f[i][j - 1] 或者f[i - 1][j - 1]得出来呢。
关键是要看S[i]和T[j]的关系了。
- 如果S[i] == T[j],那么有两种情况,要么就将这两个字符匹配,种数就有f[i - 1][j - 1]种,要么这两个字符就不匹配,种数就有f[i - 1][j]种,所以总的种数就有f[i - 1][j - 1] + f[i - 1][j]种。
- 如果S[i] != T[i], 那么这两个字符就只能不匹配,种数就有f[i - 1][j]种。
最后就要考虑初始情况了。这个二维数组的某个位置的值依赖于其左上角的值和其上一行的值。那么得初始化f[i][0]和f[0][j]的值。f[i][0]表示S[0:i]有多少个子序列与空串相同,初始化为1没毛病。f[0][j]表示S为空串,肯定不存在子序列跟T相同,因此要初始化为0。
r a b b i t
1 0 0 0 0 0 0
r 1
a 1
b 1
b 1
b 1
i 1
t 1
代码实现如下。
class Solution {
public:
int numDistinct(string s, string t) {
int s_size = s.size();
int t_size = t.size();
int distinct[s_size + 1][t_size + 1];
for(int i = 0; i <= s_size; i++){
distinct[i][0] = 1;
}
for(int i = 1; i <= t_size; i++){
distinct[0][i] = 0;
}
for(int i = 1; i <= s_size; i++){
for(int j = 1; j <= t_size; j++){
if(s[i - 1] == t[j - 1]){
distinct[i][j] = distinct[i - 1][j - 1] + distinct[i - 1][j];
}else{
distinct[i][j] = distinct[i - 1][j];
}
}
}
return distinct[s_size][t_size];
}
};
关键是怎么想出状态转移的关系,想出这个就不难了,我一开始就没想出怎么用子问题去解决父问题。耗了很长时间。这个算法实现效果还是挺好的。我看Discuss里面还有别的做法,但没有解释我就不知道是怎样的,说明可以有多种动态规划的做法的。