题目地址:
https://www.lintcode.com/problem/distinct-subsequences/description
给定两个字符串 s s s和 t t t,问 s s s中有多少个子序列等于 t t t。 s s s的一个子序列的定义是, s s s中删除若干个字符后,剩余字符按照原顺序排列所得的字符串。
思路是动态规划。假设 f [ i ] [ j ] f[i][j] f[i][j]是 s [ 0 , . . . , i − 1 ] s[0,...,i-1] s[0,...,i−1]中有多少个子序列等于 t [ 0 , . . . , j − 1 ] t[0,...,j-1] t[0,...,j−1],当 i = 0 i=0 i=0或者 j = 0 j=0 j=0的时候,就代表空串。那么很显然 f [ i ] [ 0 ] = 1 f[i][0]=1 f[i][0]=1(对应的是删光),并且 f [ 0 ] [ j ] = 0 f[0][j]=0 f[0][j]=0。注意, f [ 0 ] [ 0 ] f[0][0] f[0][0]定义为 1 1 1,因为空串也视为是空串的子序列。
接下来考虑递推方程。对于
s
[
0
,
.
.
.
,
i
−
1
]
s[0,...,i-1]
s[0,...,i−1],我们可以将它的子序列分为两类,一类是包含
s
[
i
−
1
]
s[i-1]
s[i−1]的,另一类是不包含的。那么我们有:
1、当
s
[
i
−
1
]
=
t
[
j
−
1
]
s[i-1]=t[j-1]
s[i−1]=t[j−1]的时候,包含
s
[
i
−
1
]
s[i-1]
s[i−1]且等于
t
[
0
,
.
.
.
,
j
−
1
]
t[0,...,j-1]
t[0,...,j−1]的子序列的个数为
f
[
i
−
1
]
[
j
−
1
]
f[i-1][j-1]
f[i−1][j−1],不包含
s
[
i
−
1
]
s[i-1]
s[i−1]且等于
t
[
0
,
.
.
.
,
j
−
1
]
t[0,...,j-1]
t[0,...,j−1]的子序列的个数为
f
[
i
−
1
]
[
j
]
f[i-1][j]
f[i−1][j],所以有
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
−
1
]
+
f
[
i
−
1
]
[
j
]
f[i][j]=f[i-1][j-1]+f[i-1][j]
f[i][j]=f[i−1][j−1]+f[i−1][j];
2、当
s
[
i
−
1
]
≠
t
[
j
−
1
]
s[i-1]\ne t[j-1]
s[i−1]=t[j−1]的时候,很显然等于
t
[
0
,
.
.
.
,
j
−
1
]
t[0,...,j-1]
t[0,...,j−1]的
s
s
s的子序列一定不能包含
s
[
i
−
1
]
s[i-1]
s[i−1],所以此时
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
f[i][j]=f[i-1][j]
f[i][j]=f[i−1][j]。
代码如下:
public class Solution {
/**
* @param S: A string
* @param T: A string
* @return: Count the number of distinct subsequences
*/
public int numDistinct(String S, String T) {
// write your code here
int l1 = S.length(), l2 = T.length();
int[][] dp = new int[l1 + 1][l2 + 1];
for (int i = 0; i <= l1; i++) {
for (int j = 0; j <= l2; j++) {
if (j == 0) {
dp[i][j] = 1;
} else if (i == 0) {
dp[i][j] = 0;
} else {
if (S.charAt(i - 1) == T.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
}
return dp[l1][l2];
}
}
时空复杂度 O ( m n ) O(mn) O(mn), m m m和 n n n分别为两个字符串的长度。
下面我们考虑空间优化。我们发现,每次更新 f [ i ] [ j ] f[i][j] f[i][j]的时候,只用到了其正上方和正上方左边的数据,所以只需要让 j j j从大到小来更新即可,这样每次用到的 f [ j − 1 ] f[j-1] f[j−1]的数据是上一行的数据,符合要求。代码如下:
public class Solution {
/**
* @param S: A string
* @param T: A string
* @return: Count the number of distinct subsequences
*/
public int numDistinct(String S, String T) {
// write your code here
int l1 = S.length(), l2 = T.length();
int[] dp = new int[l2 + 1];
for (int i = 0; i <= l1; i++) {
for (int j = l2; j >= 0; j--) {
if (j == 0) {
dp[j] = 1;
} else if (i == 0) {
dp[j] = 0;
} else {
if (S.charAt(i - 1) == T.charAt(j - 1)) {
dp[j] += dp[j - 1];
}
}
}
}
return dp[l2];
}
}
时间复杂度不变,空间 O ( n ) O(n) O(n), n n n为 t t t的长度。