Given a string S and a string T, count the number of distinct subsequences of S which equals T.
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).
Example 1:
Input: S = "rabbbit", T = "rabbit"
Output: 3
Explanation:
As shown below, there are 3 ways you can generate "rabbit" from S.
(The caret symbol ^ means the chosen letters)
rabbbit
^^^^ ^^
rabbbit
^^ ^^^^
rabbbit
^^^ ^^^
Example 2:
Input: S = "babgbag", T = "bag"
Output: 5
Explanation:
As shown below, there are 5 ways you can generate "bag" from S.
(The caret symbol ^ means the chosen letters)
babgbag
^^ ^
babgbag
^^ ^
babgbag
^ ^^
babgbag
^ ^^
babgbag
^^^
该题给定字符串S和字符串T,计算在S的子序列中T出现的个数。
一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)。
该题分类为动态规划,所以我们很自然地往DP方面延伸思考,如何将问题转化为子问题并得出状态转移方程。
我们用DP(i, j)来表示 字符串S的前i个字符组成的字符串的子序列中,T的前j个字符组成的字符串出现的次数(比较拗口但是不难理解,即将题目所给S,T变为S[0:i], T[0:j])。
我们很容易看出边界情况:
然后,接下来便是对状态转移方程的得出:
得出边界情况和状态方程之后,我们便可以求出答案DP(i, j)了。
class Solution {
public:
int numDistinct(string s, string t) {
int len1 = s.size(), len2 = t.size();
if (len1 == 0 || len2 == 0)
return 0;
int ** dp = new int*[len1+1];
for (int i = 0; i < len1+1; i++) {
dp[i] = new int[len2+1];
for (int j = 0; j < len2+1; j++)
dp[i][j] = 0;
}
// 边界情况
for (int i = 0; i < len1; i++)
dp[i][0] = 1;
// 根据状态转移方程递推
for (int i = 1; i <= len1; i++)
for (int j = 1; j <= len2; j++) {
if (s[i-1] == t[j-1]) {
dp[i][j] = dp[i-1][j] + dp[i-1][j-1];
} else {
dp[i][j] = dp[i-1][j];
}
}
return dp[len1][len2];
}
};
Given two arrays of length m and n with digits 0-9 representing two numbers. Create the maximum number of length k <= m + n from digits of the two. The relative order of the digits from the same array must be preserved. Return an array of the k digits.
Note: You should try to optimize your time and space complexity.
Example 1:
Input:
nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
Output:
[9, 8, 6, 5, 3]
Example 2:
Input:
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
Output:
[6, 7, 6, 0, 4]
Example 3:
Input:
nums1 = [3, 9]
nums2 = [8, 9]
k = 3
Output:
[9, 8, 9]
给定两个数组。现在从这两个数组中选出 k个数字拼接成一个新的数,要求从同一个数组中取出的数字保持其在原数组中的相对顺序。
此题虽然分类于DP,但是苦思冥想也难以划分子问题,得出状态转移方程。
然而可以采用一种比较暴力巧妙的方法,即题目要求从两个数组中取k个数字,那么可以将k分为两部分:i个和k-i个,然后从两个数组中分别取出i和k-i个,再将两个部分merge一下便可以了,然后 i 从 0 到 k进行遍历,得到所有的k+1种情况,从中挑选出最大的情况即可。
问题的难点在于如何实现从一个数组选出最大的前i个,merge操作以及比较两种情况哪个比较大的操作。
class Solution {
public:
vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
int len1 = nums1.size(), len2 = nums2.size();
if (len1 + len2 == k)
return merge(nums1, nums2);
vector<int> result;
for (int i = 0; i <= k; i++) {
if (i <= len1 && (k-i) <= len2) {
auto maxSeq1 = maxSeqOfArray(nums1, i);
auto maxSeq2 = maxSeqOfArray(nums2, k - i);
auto maxSeq = merge(maxSeq1, maxSeq2);
if (compare(maxSeq, 0, result, 0))
result = maxSeq;
}
}
return result;
}
vector<int> maxSeqOfArray(vector<int> array, int num) {
vector<int> res;
int nowIndex = 0, len = array.size();
while(num > 0) {
int max = -1, max_index;
for (int i = nowIndex; i <= len - num; i++) {
if (array[i] > max) {
max = array[i];
max_index = i;
}
}
res.push_back(max);
nowIndex = max_index + 1;
num--;
}
return res;
}
vector<int> merge(vector<int>& nums1, vector<int>& nums2) {
vector<int> res;
int len1 = nums1.size(), len2 = nums2.size(), i = 0, j = 0;
while(i < len1 || j < len2) {
if (i == len1) {
res.push_back(nums2[j++]);
} else if (j == len2) {
res.push_back(nums1[i++]);
} else if (compare(nums1, i, nums2, j)) {
res.push_back(nums1[i++]);
} else {
res.push_back(nums2[j++]);
}
}
return res;
}
bool compare(vector<int>& nums1, int index1, vector<int>& nums2, int index2) {
int len1 = nums1.size() - index1, len2 = nums2.size() - index2;
int nums1_len = nums1.size(), nums2_len = nums2.size();
if (len1 <= 0) return false;
if (len2 <= 0) return true;
int len = max(len1, len2);
for (int i = 0; i < len; i++) {
int digit1 = index1 + i < nums1_len ? nums1[index1 + i] : 0;
int digit2 = index2 + i < nums2_len ? nums2[index2 + i] : 0;
if(digit1 != digit2){
return digit1 > digit2;
}
}
return true;
}
};