惠州技术支持网站建设,网站开发设计心得,濮阳新闻综合频道直播,新手如何做好网络营销推广day54392.判断子序列1.确定dp数组#xff08;dp table#xff09;以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例推导dp数组115.不同的子序列1.确定dp数组#xff08;dp table#xff09;以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺…
day54392.判断子序列1.确定dp数组dp table以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例推导dp数组115.不同的子序列1.确定dp数组dp table以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例推导dp数组392.判断子序列
题目链接 **解题思路**动态规划五部曲分析如下
1.确定dp数组dp table以及下标的含义
dp[i][j] 表示以下标i-1为结尾的字符串s和以下标j-1为结尾的字符串t相同子序列的长度为dp[i][j]。
注意这里是判断s是否为t的子序列。即t的长度是大于等于s的。
有同学问了为啥要表示下标i-1为结尾的字符串呢为啥不表示下标i为结尾的字符串呢
2.确定递推公式
在确定递推公式的时候首先要考虑如下两种操作整理如下
if (s[i - 1] t[j - 1]) t中找到了一个字符在s中也出现了 if (s[i - 1] ! t[j - 1]) 相当于t要删除元素继续匹配
if (s[i - 1] t[j - 1])那么dp[i][j] dp[i - 1][j - 1] 1;因为找到了一个相同的字符相同子序列长度自然要在dp[i-1][j-1]的基础上加1如果不理解在回看一下dp[i][j]的定义
if (s[i - 1] ! t[j - 1])此时相当于t要删除元素t如果把当前元素t[j - 1]删除那么dp[i][j] 的数值就是 看s[i - 1]与 t[j - 2]的比较结果了即dp[i][j] dp[i][j - 1];
3.dp数组如何初始化
从递推公式可以看出dp[i][j]都是依赖于dp[i - 1][j - 1] 和 dp[i][j - 1]所以dp[0][0]和dp[i][0]是一定要初始化的。
这里大家已经可以发现在定义dp[i][j]含义的时候为什么要表示以下标i-1为结尾的字符串s和以下标j-1为结尾的字符串t相同子序列的长度为dp[i][j]。
因为这样的定义在dp二维矩阵中可以留出初始化的区间如图
如果要是定义的dp[i][j]是以下标i为结尾的字符串s和以下标j为结尾的字符串t初始化就比较麻烦了。
dp[i][0] 表示以下标i-1为结尾的字符串与空字符串的相同子序列长度所以为0. dp[0][j]同理。
vectorvectorint dp(s.size() 1, vectorint(t.size() 1, 0));4.确定遍历顺序
同理从递推公式可以看出dp[i][j]都是依赖于dp[i - 1][j - 1] 和 dp[i][j - 1]那么遍历顺序也应该是从上到下从左到右 如图所示
5.举例推导dp数组
以示例一为例输入s “abc”, t “ahbgdc”dp状态转移图如下
dp[i][j]表示以下标i-1为结尾的字符串s和以下标j-1为结尾的字符串t 相同子序列的长度所以如果dp[s.size()][t.size()] 与 字符串s的长度相同说明s与t的最长相同子序列就是s那么s 就是 t 的子序列。
图中dp[s.size()][t.size()] 3 而s.size() 也为3。所以s是t 的子序列返回true。
动规五部曲分析完毕C代码如下
class Solution {
public:bool isSubsequence(string s, string t) {vectorvectorint dp(s.size() 1, vectorint(t.size() 1, 0));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] 1;else dp[i][j] dp[i][j - 1];}}if (dp[s.size()][t.size()] s.size()) return true;return false;}
};115.不同的子序列
题目链接 解题思路
动规五部曲分析如下
1.确定dp数组dp table以及下标的含义
dp[i][j]以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。
2.确定递推公式
这一类问题基本是要分析两种情况
s[i - 1] 与 t[j - 1]相等s[i - 1] 与 t[j - 1] 不相等
当s[i - 1] 与 t[j - 1]相等时dp[i][j]可以有两部分组成。
一部分是用s[i - 1]来匹配那么个数为dp[i - 1][j - 1]。即不需要考虑当前s子串和t子串的最后一位字母所以只需要 dp[i-1][j-1]。
一部分是不用s[i - 1]来匹配个数为dp[i - 1][j]。
例如 sbagg 和 tbag s[3] 和 t[2]是相同的但是字符串s也可以不用s[3]来匹配即用s[0]s[1]s[2]组成的bag。
当然也可以用s[3]来匹配即s[0]s[1]s[3]组成的bag。
所以当s[i - 1] 与 t[j - 1]相等时dp[i][j] dp[i - 1][j - 1] dp[i - 1][j];
当s[i - 1] 与 t[j - 1]不相等时dp[i][j]只有一部分组成不用s[i - 1]来匹配就是模拟在s中删除这个元素即dp[i - 1][j]
所以递推公式为dp[i][j] dp[i - 1][j];
3.dp数组如何初始化
从递推公式dp[i][j] dp[i - 1][j - 1] dp[i - 1][j]; 和 dp[i][j] dp[i - 1][j]; 中可以看出dp[i][j] 是从上方和左上方推导而来如图那么 dp[i][0] 和dp[0][j]是一定要初始化的。
每次当初始化的时候都要回顾一下dp[i][j]的定义不要凭感觉初始化。
dp[i][0]表示什么呢
dp[i][0] 表示以i-1为结尾的s可以随便删除元素出现空字符串的个数。
那么dp[i][0]一定都是1因为也就是把以i-1为结尾的s删除所有元素出现空字符串的个数就是1。
再来看dp[0][j]dp[0][j]空字符串s可以随便删除元素出现以j-1为结尾的字符串t的个数。
那么dp[0][j]一定都是0s如论如何也变成不了t。
最后就要看一个特殊位置了即dp[0][0] 应该是多少。
dp[0][0]应该是1空字符串s可以删除0个元素变成空字符串t。
初始化分析完毕代码如下
vectorvectorlong long dp(s.size() 1, vectorlong long(t.size() 1));
for (int i 0; i s.size(); i) dp[i][0] 1;
for (int j 1; j t.size(); j) dp[0][j] 0; // 其实这行代码可以和dp数组初始化的时候放在一起但我为了凸显初始化的逻辑所以还是加上了。4.确定遍历顺序
从递推公式dp[i][j] dp[i - 1][j - 1] dp[i - 1][j]; 和 dp[i][j] dp[i - 1][j]; 中可以看出dp[i][j]都是根据左上方和正上方推出来的。
所以遍历的时候一定是从上到下从左到右这样保证dp[i][j]可以根据之前计算出来的数值进行计算。
代码如下
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];}}
}5.举例推导dp数组
以s“baegg”tbag为例推导dp数组状态如下
动规五部曲分析完毕代码如下
class Solution {
public:int numDistinct(string s, string t) {vectorvectoruint64_t dp(s.size() 1, vectoruint64_t(t.size() 1));for (int i 0; i s.size(); i) dp[i][0] 1;for (int j 1; j t.size(); j) dp[0][j] 0;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()];}
};