![[01背包]leetcode115:不同的子序列(hard),第1张 [01背包]leetcode115:不同的子序列(hard),第1张](/aiimages/%5B01%E8%83%8C%E5%8C%85%5Dleetcode115%EF%BC%9A%E4%B8%8D%E5%90%8C%E7%9A%84%E5%AD%90%E5%BA%8F%E5%88%97%28hard%29.png)
题目:
题解:
思路:01背包
- 本题的实质就是一个01背包模型题,字符串 s 为总物品,字符串 t 为背包体积,问能恰好装满体积 t 的物品方案数。
分析出01背包后,状态定义和状态转移方程就好解释了。
- 状态
f[i][j]:表示从s[0...i]从选能装满体积t[0...j]的集合方案数;- 状态转移:字符串
s中第i个物品选与不选来进行转移。不选s[i],那么方案数直接等于f[i-1][j],表示从s[0...i-1]中选总体积恰好为t[0...j]的方案数;选择s[i],仅需满足s[i]==t[j]的条件,这样方案数需要加上f[i-1][j-1],表示除去t[i]后,从s[0...i-1]中选总体积恰好为t[0...j-1]的方案数;
代码如下:
class Solution {
public:
// 01背包模型:s为物品总数,t为背包容量
int numDistinct(string& s, string& t) {
return dp2(s,t);
}
// 优化为一维空间的dp
int dp2(string& s,string &t)
{
int n=s.size(),m=t.size();
// 头部插空格是为了让下标从1开始递推
s=" "+s,t=" "+t;
// 优化掉物品维度
vector<int> f(m+1,0);
// 初始化状态:s[0]=' '在t中子序列个数只有1个
f[0]=1;
// 进行状态转移,先遍历物品,再遍历背包
for(int i=1;i<=n;++i)
for(int j=m;j;j--)// 倒序遍历体积,倒叙来确保f[j-1]是上一层未被更新的状态即f[i-1][j-1]
if(s[i]==t[j])f[j]=(0LL+f[j]+f[j-1])%INT_MAX;
return f[m];
}
// 二维空间的dp
int dp1(string& s,string& t)
{
int n=s.size(),m=t.size();
// 头部插空格是为了让下标从1开始递推
s=" "+s,t=" "+t;
// f[i][j]表示t[0...j]在s[0...i]中子序列的个数
vector<vector<int>> f(n+1,vector<int>(m+1,0));
// 初始化状态:s[0]=' '在t中子序列个数只有1个
for(int i=0;i<=n;++i)f[i][0]=1;
// 进行状态转移,先遍历物品,再遍历背包
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
// 不选s[i],那么s[0...i-1]与t[0...j]匹配
f[i][j]=f[i-1][j];
// 选择s[i],那么s[0...i-1]与t[0...j-1]匹配
if(s[i]==t[j])f[i][j]=(0LL+f[i-1][j-1]+f[i][j])%INT_MAX;
}
return f[n][m];
}
};
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)