首页 > 代码库 > 【leetcode】Distinct Subsequences

【leetcode】Distinct Subsequences

问题:给定两个字符串S,T,对于S,可以删除其中的任意多个(包括0)字符,使其得到T。问有多少种删法可以得到T。

举例分析:

S:ababa

T: aba

dp[i][j] : 表示 S 从0 ~ i - 1,T从0~j - 1,所得到的方法数。i,j 表示长度。
初始条件:dp[i][0] = 1,T为空串,而空串总是任意串的字串。即,将S串的所有字符都删掉,就得到T。
状态转移方程:
dp[ i ] [ j ] = dp[ i - 1] [ j - 1] + dp[ i - 1] [ j ]  ; if  S[i - 1] == T[ j - 1]
dp[ i ] [ j ] = dp[i - 1] [ j ]; else.

第一个状态转移方程表示,当S[i - 1] == T[ j - 1]时,我可以将T[j - 1] 认为是从S[i - 1] 得到,也可以认为不是。这是两种情况,最后的结果应该是两种情况的和。



按着状态转移方程,我们通过填表,得到结果如上图所示,其中第一列黑色的 1 为初始状态。每一行相同的颜色是我们通过一次遍历得到的结果。最终的结果为 右下角的 4.

//code

int numDistinct( string S,  string T){

	int lenT = T.size();
	int lenS = S.size();
	if(lenS < lenT)  return 0;
	vector<vector<int> > dp(lenS + 1, vector<int>(lenT + 1, 0));
	for (int i = 0; i <= lenS; ++i)
	    dp[i][0] = 1;
	for (int i = 1; i <= lenS; ++i)
		for(int j = 1; j <= lenT; ++j)
		{
			if(T[j - 1] == S[i - 1])
				dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
			else
				dp[i][j] = dp[i - 1][j];
		}
	
	return dp[lenS][lenT];
}

通过状态转移方程,我们发现, 第 i 行只跟第 i - 1 行有关系,所以可以简化dp表,只用两行来进行迭代。但是这样每一行的第一个元素都很特别,都是 1 ,就使得我们在迭代的时候稍微麻烦些,我们换一下行列。当然我们也可以按列填表,但是我们总是习惯按行处理,至少笔者如此。


如此一来,所有的初始化就在同一行了,

int numDistinct(string S, string T) {
	
	int lenT = T.size();
	int lenS = S.size();
	if(lenS < lenT)  return 0;

	//dp[0][i] = 1.
	vector<int> dp_pre(lenS + 1, 1);
	
	for (int i = 1; i <= lenT; ++i)
	{
		vector<int> dp_cur(lenS + 1, 0);
		for (int j = 1; j <= lenS; ++j)
		{
			if(T[i - 1] == S[j - 1])
				dp_cur[j] = dp_pre[j - 1] + dp_cur[j - 1];
			else
				dp_cur[j] = dp_cur[j - 1];
		}
		dp_pre = dp_cur;
	}
	return dp_pre[lenS];
	
}