一. Distinct Subsequences
Given a string S and a string T, count the number of distinct subsequences of T in S.
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).
Here is an example:
S = “rabbbit”, T = “rabbit”
Return 3.
Difficulty:Hard
TIME:TIMEOUT
解法一
这道题目的意思并不是那么容易理解,其实这道题求的是在字符串中S,找出子序列能够组成T,求子序列的数目。
鉴于之前求过一个字符串的所有递增子序列Increasing Subsequences,因此也可以用类似的递归方法来求特定的子序列,结果果真超时了。
void dfs(string s, string t, int index, int left, int &num) {
if(index == t.size()) {
num++;
return;
}
for(size_t i = left; i < s.size(); i++) {
if(s[i] == t[index]) {
dfs(s, t, index + 1, i + 1, num);
}
}
}
int numDistinct(string s, string t) {
int num = 0;
dfs(s, t, 0, 0, num);
return num;
}
代码的时间复杂度约为
O(nkm)
,其中
k
为字符串
优化
后面发现,其实递归程序遍历了很多可以不用遍历的地方,比如对于字符串
如果采用一个二维数组
result=m[0][0]+m[2][0]
m[0][0]=m[1][1]+m[3][1]
m[1][1]=m[2][2]+m[4][2]
m[3][1]=m[4][2]
m[2][0]=m[3][1]
m[3][1]=m[4][2] 这一步可以忽略了,在求 m[0][0] 的时候已经求过这个值了
因此这就是这道题动态规划的一种最优子结构,也就是相同的后缀如果已经求过,就可以不用求了。最后我们采取带备忘的递归程序就可以解决这道题。
int dfs(string s, string t, int index, int left, map<int,map<int,int>>& m) {
if(index == t.size())
return 1;
int num = 0;
for(size_t i = left; i < s.size(); i++) {
if(s[i] == t[index]) {
if(m[i].find(index) == m[i].end())
m[i][index] = dfs(s, t, index + 1, i + 1,m);
num += m[i][index];
}
}
return num;
}
int numDistinct(string s, string t) {
map<int,map<int,int>> m;
int num = dfs(s, t, 0, 0, m);
return num;
}
时间复杂度理论上应该是 O(mn) 。
解法二
这道题的第一种最优子结构考虑相同后缀的情况,而另一种最优子结构则考虑相同前缀的情况。
也采用相同的二维数组
m
,不过这里第一维表示字符串
因此很容易想到
- 如果 t[i−1]=s[j−1] ,那么 m[i][j]=m[i][j−1]+m[i−1][j−1]
- 否则, m[i][j]=m[i][j−1]
还是拿之前的例子
s=ababc
,
t=abc
来说明
对于
m[1][1]
,也就是说
s=a
而且
t=a
,那么显然
s[0]=t[0]
,那么
m[1][1]=m[1][0]+m[0][0]=1
对于
m[1][2]
,也就是说
s=ab
而且
t=a
,那么显然
s[1]!=t[0]
,那么
m[1][2]=m[1][1]=1
对于
m[1][3]
,也就是说
s=aba
而且
t=a
,那么显然
s[2]=t[0]
,那么
m[1][3]=m[0][2]+m[1][2]=2
….
对于
m[2][1]
,也就是说
s=a
而且
t=ab
,那么显然
s[0]!=t[1]
,那么
m[2][1]=m[2][0]=0
对于
m[2][2]
,…,
m[2][2]=m[1][1]+m[2][1]=1
对于
m[2][3]
,…,
m[2][3]=m[2][2]=1
对于
m[2][4]
,…,
m[2][4]=m[2][3]+m[1][3]=3
….
因此这样的二维数组为
111111
011222
001133
000003
结果确实只有3个
<ab∗∗c,a∗∗bc,∗∗abc>
<script type="math/tex" id="MathJax-Element-14530">
</script>,代码如下:
int numDistinct(string s, string t) {
if(s.size() < t.size())
return 0;
vector<vector<int>> dp(t.size() + 1,vector<int>(s.size() + 1, 0));
for(size_t i = 0; i < s.size(); i++) {
dp[0][i] = 1;
}
for(size_t i = 1; i <= t.size(); i++) {
for(size_t j = 1; j <= s.size(); j++) {
if(t[i - 1] == s[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1];
}
else {
dp[i][j] = dp[i][j - 1];
}
}
}
return dp[t.size()][s.size()];
}
时间复杂度为 O(mn) 。
知识点
动态规划