115. Distinct Subsequence
原题目描述
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(0, 0) = 1,因为此时T和S对应是空串,空串只在空串中出现一次;
- DP(i, 0) = 1, i > 0, 因为此时T为空串,而S不为空串,空串只在S中出现一次;
- DP(0, j) = 0, j > 0, 因为此时S为空串,而T不为空串,T在空串中不可能出现过;
然后,接下来便是对状态转移方程的得出:
- 当S[i] != T[j]的时候,DP(i, j) = DP(i-1, j),例如:“ABCD”的子序列中匹配“AB”的个数,与"ABC"的子序列匹配“AB”的个数是相同的。
- 当S[i] == T[j]的时候,DP(i, j) = DP(i-1, j) + DP(i-1, j-1),这个也很好理解,例如,“ABCC”中匹配“”ABC"的数量,除了其子序列"ABC"中匹配“ABC”的数量,还要考虑"ABC"中匹配“AB”的数量。(即最后一个字符可能是前面就匹配过的又重复了的,也可以是现在才匹配的,需要考虑这两种情况)。
得出边界情况和状态方程之后,我们便可以求出答案DP(i, j)了。
C++实现代码
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];
}
};
321. Create Maximum Number
原题目描述
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操作以及比较两种情况哪个比较大的操作。
- 如何从数组中挑选出前 i 个数字组成最大的数,因为要求这i个在数组中的位置相对不变,那么很容易可以知道,求第一个便是在前len -i个中挑选最大的那一个,后边的也就变成子问题了(也许这一步有DP的可能)。
- merge操作,这个其实不难,每次比较哪个数组比较大(比较操作看下一步),然后从那个情况中pop出第一个,然后接着比较和pop,知道两个数组都为空了。‘’
- 比较操作也很简单,就只是单纯依次对数组中每一个数字,一个一个地进行比较而已,考虑好边界情况即可(数组为空或不一样长)。
C++实现代码
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;
}
};