2014.2.8 22:11
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
.
Solution1:
The first solution I think of is by recursive searching. After counting the occurrence of every alphabet and recording the indices, we can solve the problem by a recursive search, which ends when the total string T is matched.
After testing the code myself, it ran well under small scale, but slowed down in front of long strings, exactly as I expected.
If we're to find out a more efficient solution, dynamic programming would be a better strategy:
1. Let dp[x][y] be the number of possible combinations of T[1:y] in S[1:x].
2. dp[x][0] = 0.
3. dp[0][y] = 0.
4. if S[x] = T[1], dp[x][1] = dp[x - 1][1] + 1, else dp[x][1] = dp[x - 1][1].
5. if S[x] = T[y], dp[x][y] = dp[x - 1][y] + dp[x - 1][y - 1], else dp[x][y] = dp[x - 1][y].
The dynamic programming is O(n^2) scale.
Time and space complexities are both O(len(S) * len(T)).
Accepted code:
1 // 2RE, 1WA, 1AC, don't use memset on dp[][], although it looks as if it were one block of memory. 2 #include <cstring> 3 #include <vector> 4 using namespace std; 5 6 class Solution { 7 public: 8 int numDistinct(string S, string T) { 9 int **dp; 10 int ls, lt; 11 int result; 12 int i, j; 13 14 ls = (int)S.size(); 15 lt = (int)T.size(); 16 if (ls == 0 || lt == 0) { 17 return 0; 18 } 19 20 dp = new int*[ls + 1]; 21 for (i = 0; i < ls + 1; ++i) { 22 dp[i] = new int[lt + 1]; 23 } 24 for (i = 0; i <= ls; ++i) { 25 for (j = 0; j <= lt; ++j) { 26 dp[i][j] = 0; 27 } 28 } 29 /* 30 for (i = 0; i <= ls; ++i) { 31 dp[i][0] = 0; 32 } 33 for (i = 0; i <= lt; ++i) { 34 dp[0][i] = 0; 35 } 36 */ 37 for (i = 1; i <= ls; ++i) { 38 if (S[i - 1] == T[0]) { 39 dp[i][1] = dp[i - 1][1] + 1; 40 } else { 41 dp[i][1] = dp[i - 1][1]; 42 } 43 } 44 45 for (i = 2; i <= ls; ++i) { 46 for (j = 2; j <= lt; ++j) { 47 if (S[i - 1] == T[j - 1]) { 48 dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1]; 49 } else { 50 dp[i][j] = dp[i - 1][j]; 51 } 52 } 53 } 54 result = dp[ls][lt]; 55 for (i = 0; i < ls + 1; ++i) { 56 delete[] dp[i]; 57 } 58 delete[] dp; 59 dp = NULL; 60 61 return result; 62 } 63 };
Solution2:
The dynamic programming version can be optimized to linear space complexity, using only two rows, switching alternatively.
Time complexity is O(len(S) * len(T)). Space complexity is O(len(T)).
Accepted code:
1 // 2RE, 1WA, 1AC, don't use memset on dp[][], although it looks as if it were one block of memory. 2 #include <cstring> 3 #include <vector> 4 using namespace std; 5 6 class Solution { 7 public: 8 int numDistinct(string S, string T) { 9 int **dp; 10 int ls, lt; 11 int result; 12 int i, j; 13 14 ls = (int)S.size(); 15 lt = (int)T.size(); 16 if (ls == 0 || lt == 0 || ls < lt) { 17 return 0; 18 } 19 20 dp = new int*[2]; 21 for (i = 0; i < 2; ++i) { 22 dp[i] = new int[lt + 1]; 23 } 24 for (i = 0; i < 2; ++i) { 25 for (j = 0; j <= lt; ++j) { 26 dp[i][j] = 0; 27 } 28 } 29 30 if (S[0] == T[0]) { 31 dp[1][1] = 1; 32 } 33 34 int flag; 35 flag = 0; 36 for (i = 2; i <= ls; ++i) { 37 dp[flag][0] = 0; 38 if (S[i - 1] == T[0]) { 39 dp[flag][1] = dp[!flag][1] + 1; 40 } else { 41 dp[flag][1] = dp[!flag][1]; 42 } 43 for (j = 2; j <= lt; ++j) { 44 if (S[i - 1] == T[j - 1]) { 45 dp[flag][j] = dp[!flag][j] + dp[!flag][j - 1]; 46 } else { 47 dp[flag][j] = dp[!flag][j]; 48 } 49 } 50 flag = !flag; 51 } 52 flag = !flag; 53 result = dp[flag][lt]; 54 for (i = 0; i < 2; ++i) { 55 delete[] dp[i]; 56 } 57 delete[] dp; 58 dp = NULL; 59 60 return result; 61 } 62 };