week10

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[s.size] [t.size] ;
好了,现在就得思考子问题了,f[i][j]到底可不可以通过f[i - 1][j], f[i][j - 1] 或者f[i - 1][j - 1]得出来呢。
关键是要看S[i]和T[j]的关系了。

  1. 如果S[i] == T[j],那么有两种情况,要么就将这两个字符匹配,种数就有f[i - 1][j - 1]种,要么这两个字符就不匹配,种数就有f[i - 1][j]种,所以总的种数就有f[i - 1][j - 1] + f[i - 1][j]种。
  2. 如果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

代码实现如下。

扫描二维码关注公众号,回复: 4658218 查看本文章
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里面还有别的做法,但没有解释我就不知道是怎样的,说明可以有多种动态规划的做法的。

猜你喜欢

转载自blog.csdn.net/pjsfirstlaw/article/details/83042855