「这是我参与2022首次更文挑战的第26天,活动详情查看:2022首次更文挑战」
题目
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
示例 1: 输入:s = "abc", t = "ahbgdc" 输出:true
示例 2: 输入:s = "axc", t = "ahbgdc" 输出:false
提示:
- 0 <= s.length <= 100
- 0 <= t.length <= 10^4
两个字符串都只由小写字符组成。
解题思路
此题同样是明显的动态规划题目,而且我们分析题目观察几个示例,可以发现这道题就是LeetCode1143.最长公共子序列变型题目,其思路与LeetCode1143.最长公共子序列完全一致,只需要改改变量名就行。在最长公共子序列这道题目里我们可以获得两个字符串的最长公共子序列长度,而对于判断子序列这件事来说,只要两个字符串的最长公共子序列长度等于短字符串长度,就证明了短字符串是长字符串的子序列
- dp[i][j]的定义
dp[i][j]表示s字符串索引i前和t字符串索引j前有多少对相同的字母
- 状态转移方程
- 主要就是两大情况: s[i - 1] 与 t[j - 1]相同,s[i - 1] 与 t[j - 1]不相同 如果s[i - 1] 与 t[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1;
- 如果s[i - 1] 与 t[j - 1]不相同,那就看看s[0, i - 2]与t[0, j - 1]的最长公共子序列 和 s[0, i - 1]与t[0, j - 2]的最长公共子序列,取最大的。 即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
- dp数组如何初始化
s[0, i-1]和空串的最长公共子序列自然是0,所以dp[i][0] = 0;同理dp[0][j]也是0。其他下标都是随着递推公式逐步覆盖,初始为多少都可以,那么就统一初始为0。
- 确定遍历顺序
从递推公式,可以看出,有三个方向可以推出dp[i][j],左边,左上方,上方,所以为了在递推的过程中,这三个方向都是经过计算的数值,所以要从前向后,从上到下来遍历这个矩阵
最后,我们用得到的最长子序列长度与短字符串长度对比,如果相等,证明此短字符串是长字符串的子序列
var isSubsequence = function(s, t) {
let la = s.length;
let lb = t.length;
let dp = Array.from(Array(la + 1), () => Array(lb + 1).fill(0));
for (let i = 1; i <= la; i++) {
for (let j = 1; j <= lb; j++) {
if (s[i - 1] == t[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}else{
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[la][lb] === la? true: false;
};