115. Distinct Subsequences
状态转移方程:
理解:
dp[i][j]表示s[0:j]和t[0:i]的结果
当s[j] 与 t[i]不相等时,当前结果即为前一轮结果
当两者相等时,当前结果可以分为两个情况:
s[j] 与 t[i] 匹配,此时对应的结果为dp[i - 1][j - 1]
s[j] 不与 t[i] 匹配,此时的结果为dp[i][j - 1]
因此最终的结果为两者的和
举个例子:
先补一行一列,第0行表示当t为空时,都可以匹配,第0列表示当s为空时无法匹配
0 r b b b t (s)
0 1 1 1 1 1 1
r 0 1 1 1 1 1
b 0 0 1 2 3 3
t 0 0 0 0 0 3
(t)
s = rbbbt,t=rbt,当遍历到t[i] = ‘b’ s[j]=‘b’(第二个)时,此时有两个选择:t[i]与s[j]匹配,即
rbb
^ ^
此时的dp[i][j] = dp[i - 1][j - 1]
或者t[i]不与s[j]匹配,即
rbb
^^
此时的dp[i][j] = dp[i][j - 1]
最后的代码:
class Solution {
public:
int numDistinct(string s, string t) {
int lenS = s.length(), lenT = t.length();
vector<vector<long long>> dp(lenT + 1, vector<long long>(lenS + 1, 0));
for(int i = 0; i <= lenS; ++i)
dp[0][i] = 1;
for(int i = 1; i <= lenT; ++i)
dp[i][0] = 0;
for(int i = 1; i <= lenT; ++i)
for(int j = 1; j <= lenS; ++j)
{
if(s[j - 1] == t[i - 1]) dp[i][j] = dp[i][j - 1] + dp[i - 1][j - 1];
else dp[i][j] = dp[i][j - 1];
}
return dp[lenT][lenS];
}
};
940. Distinct Subsequences II
状态转移方程:
j的取值:[0, i)
理由:
dp[i]表示以s[i]结尾的子序列的个数
举例说明:
abca:
a: a
b: ab, b
c: ac, abc, bc, c
a: aa, aba, ba, aca, abca, bca, ca, a(相当于第一个a没做)
const int MOD = 1e9 + 7;
class Solution {
public:
int distinctSubseqII(string S) {
int result = 0;
vector<int> dp(S.length(), 1);
for(unsigned i = 0; i < S.length(); ++i)
{
for(unsigned j = 0; j < i; ++j)
{
if(S[j] != S[i])
{
dp[i] += dp[j];
dp[i] %= MOD;
}
}
result += dp[i];
result %= MOD;
}
return result;
}
};
474. Ones and Zeroes
给定一个字符串数组和两个整数m,n,其中m表示0能出现的次数上限,n表示1能出现的次数上限,给出最多数量的字符串,使其1和0的个数都满足要求
典型的二维费用背包问题,先创建一个二维数组res, res[i][j]表示只使用i个0和j个1,能得到的最大价值,每次遇到一个字符串,先分别算出0和1的数目,作为二维费用中各自的cost,且每个元素的value均为1
因此
,最后返回res[m][n]即可
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
int len = strs.size();
vector<vector<int>> res(m + 1, vector<int>(n + 1, 0));
//int maxn = 0, pos = 1;
for (string s : strs)
{
int zero = 0, one = 0;
for (char c : s)
if (c == '0') zero++;
else one++;
for(int i = m; i >= zero; --i)
for (int j = n; j >= one; --j)
{
res[i][j] = max(res[i][j], res[i - zero][j - one] + 1);
//maxn = max(res[i][j], maxn);
}
//pos++;
}
return res[m][n];
}
};
322. Coin Change
给定一个整型数组coins和一个整数amount,其中coins中的数可以用无限次,判断最少需要使用多少个coins中的数,使其之和为amount,若不存在,则返回-1。
状态转移方程:
比如例题中,
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount + 1, 1e9);
sort(coins.begin(), coins.end());
dp[0] = 0;
for(int i = 1; i <= amount; ++i)
{
int minn = 1e9;
for(int c: coins)
{
if(i - c < 0) break;
minn = min(minn, dp[i - c]);
}
dp[i] = minn + 1;
}
if(dp[amount] >= 1e9) return -1;
else return dp[amount];
}
};
416. Partition Equal Subset Sum
给定一个数组,判断其是否能被分成两个和相等的子数组
状态转移方程:
举个例子:[2, 2, 3, 4, 5]
初始化dp[0]=true
第一轮dp[2] = dp[2] || dp[2-2] = true
第二轮dp[4] = dp[4] || dp[4-2] = true
即后一项对应不使用该元素时的和
最后只要返回原数组总和sum 的 1/2即可
完整代码:
class Solution {
public:
bool canPartition(vector<int>& nums) {
int len = nums.size();
if(len < 2) return false;
long long int sum = 0;
for(int& i: nums) sum += i;
if(sum % 2) return false; // sum为奇数,则不可划分
sum /= 2;
vector<bool> dp(sum + 1, 0);
dp[0] = true; // 初始化dp[0] = true
for(int i = 0; i < len; ++i)
{
for(int j = sum; j >= nums[i]; --j)
dp[j] = dp[j] || dp[j - nums[i]];
}
return dp[sum];
}
};