1. 程式人生 > >week10

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

程式碼實現如下。

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裡面還有別的做法,但沒有解釋我就不知道是怎樣的,說明可以有多種動態規劃的做法的。