115. 不同的子序列, 大彻大悟
✅做题思路or感想
这道困扰我一刷好久的题终于在二刷的今天彻底理解了,通透啊!
这道题的难点在于递推公式和初始化
首先可以把题目按照自己的意思更大白话的复述一遍
- 题面:找
s
的子序列中t
出现的次数 - 复述:在
s
中挑选字符来组成t
,求这个操作有几种方法
dp
数组含义
dp[i][j]
表示[0, i - 1]
和[0, j - 1]
的区间上的挑选字符使之匹配的方法个数
💡递推公式
这里从最后一步开始思考。以s = baba
,t = ba
为例子,当最后一个字符a
相同时,有两种选择(从最后一步思考)
s
最后一位可以把t
的最后一位匹配掉,则下一步要缩短匹配范围,去匹配bab
和b
,即有dp[i - 1][j - 1]
- 因为
s
是长的一方,s
里面除了最后一位外可能还有其他的a
和t
的最后一位a
匹配,所以这里放弃s
的最后一位,缩短s
的搜索范围继续匹配t
的最后一位,所以有dp[i - 1][j]
- 最后因为求的是方法数,所以要把这两种可能都加起来,故有
dp[i][j] = dp[i - 1][j - 1], dp[i - 1][j]
而当s
最后一个字符匹配不掉t
的最后一个字符,则s
再向后面匹配便是了,故有dp[i][j] = dp[i - 1][j]
综上
if (s[i - 1] == t[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] //s[i - 1]匹配掉了t[j - 1]
+ dp[i - 1][j]; //s[i - 1]没有匹配掉t[j - 1]
} else {
dp[i][j] = dp[i - 1][j]; //s[i - 1]没有匹配掉t[j - 1]
}
初始化
这种字符串问题的初始化一定要联想两边是空字符串的时候的情况
s
是空字符串时,从s
中挑不出字符组成t
,所以这种情况的方法数为0,即是dp[0][j] = 0
。而例外就是如果t
也是空字符串,则有一种方法可以让他们匹配:什么也不做(认真),所以dp[0][0] = 1
,这点特别重要t
是空字符串时,从s
中挑字符组成t
只有一种方法:什么也不做(认真),所以这种情况的方法数为1,即dp[i][0] = 1
遍历顺序
从递推公式知从小推大,故正序遍历
class Solution {
public:
int numDistinct(string s, string t) {
//这里注意测试用例里有超出int范围的例子,所以这里扩大一下范围,选用unsigned int
vector<vector<unsigned>>dp (s.size() + 1, vector<unsigned>(t.size() + 1, 0));
//初始化dp[i][0] = 1的情况,另一种情况在创建数组的时候就默认为0了,所以不用再用一个for来初始化
for (int i = 0; i < s.size(); ++i) {
dp[i][0] = 1;
}
for (int i = 1; i <= s.size(); ++i) {
for (int j = 1; j <= t.size(); ++j) {
//两种情况
if (s[i - 1] == t[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[s.size()][t.size()];
}
};
这一题能理解透彻真的不容易,大彻大悟的感觉就是舒服
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!