“求子序列个数”,毋庸置疑,这是一道动态规划题。首先定义dp[i][j]
的含义:S[0..j-1]
中包含T[0..i-1]
的子序列个数,接下来定义状态公式:
- 状况1:
dp[i][j]=dp[i][j-1]
(如果T[i-1]!=S[j-1]
) - 状况2:
dp[i][j]=dp[i][j-1] + dp[i-1][j-1]
(如果T[i-1]==S[j-1]
) - 基准条件1:
dp[0][j]=1
(j>=0) - 基准条件2:
dp[i][0]=0
(i>0)
接下来详细解释以上四个等式:
- 如果
T[i-1] != S[j-1]
,那么子序列将不会包含S[j-1]
,因此所有的不同子序列会包含在S[0..j-2]
中,对应个数为dp[i][j-1]
- 如果
T[i-1] == S[j-1]
,那么子序列分两种——包含和不包含s[j-1]
- 空串在任意字串中只有一种子序列
- 非空串在任意非空串中都没有对应子序列
举一反三:遇到求子串/子序列个数问题,我们基本上都可以考虑拆解序列,只是拆解方式有所不同,子串问题一般用一维dp数组,子序列问题一般用二维dp数组,根据题目情况的变化,维度或许会增加,但是基本本题的维度还是一维或二维
代码如下:
// // Created by jt on 2020/8/25. // #include <string> #include <vector> using namespace std; class Solution { public: /** * * @param S string字符串 * @param T string字符串 * @return int整型 */ int numDistinct(string S, string T) { // write code here int m = T.size(), n = S.size(); vector<vector<int> > dp(m+1, vector<int>(n+1, 0)); for (int i = 0; i <= n; ++i) dp[0][i] = 1; // for (int i = 1; i <= m; ++i) dp[i][0] = 0; for (int i = 1; i <= m; ++i) { for (int j = 1; j <=n; ++j) { dp[i][j] = T[i-1] == S[j-1] ? dp[i][j-1] + dp[i-1][j-1] : dp[i][j-1]; } } return dp[m][n]; } };