一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第19天,点击查看活动详情。
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
进阶:
如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
示例 1:
输入:s = "abc", t = "ahbgdc"
输出:true
示例 2:
输入:s = "axc", t = "ahbgdc"
输出:false
提示:
0 <= s.length <= 1000 <= t.length <= 10^4- 两个字符串都只由小写字符组成。
动态规划
我们可以出对于 t 的每一个位置,从该位置开始往后每一个字符第一次出现的位置。
我们可以使用动态规划的方法实现预处理,令 表示字符串 t 中从位置 i 开始往后字符 j 第一次出现的位置。在进行状态转移时,如果 t 中位置 i 的字符就是 j,那么 ,否则 j 出现在位置 i+1 开始往后,即 ,因此我们要倒过来进行动态规划,从后往前枚举 i。
这样我们可以写出状态转移方程:
假定下标从 0 开始,那么 中有 ,对于边界状态 ,我们置 为 m,让 正常进行转移。这样如果 ,则表示从位置 i 开始往后不存在字符 j。
这样,我们可以利用 f 数组,每次 O(1) 地跳转到下一个位置,直到位置变为 m 或 s 中的每一个字符都匹配成功。
const isSubsequence = (s, t) => {
// s、t的长度
const [m, n] = [s.length, t.length];
// dp全初始化为0
const dp = new Array(m + 1).fill(0).map(x => new Array(n + 1).fill(0));
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
// 更新dp[i][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];
}
}
}
// 遍历结束,判断dp右下角的数是否等于s的长度
return dp[m][n] === m ? true : false;
};