【算法-LeetCode】392. 判断子序列(indexOf;动态规划;双指针)

392. 判断子序列 - 力扣(LeetCode)

文章更新:2021年10月22日22:30:05

问题描述及示例

给定字符串 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 <= 100
0 <= t.length <= 104
两个字符串都只由小写字符组成。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/is-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

我的题解

我的题解1(暴力解法/indexOf)

一上来的思路非常简单,就是遍历 s 里的字符,看 t 中是否有当前遍历字符。如果没有的话,就直接返回 false;如果有的话获取相应字符的下标 start,然后从 start 开始寻找 s 中的下一个字符是否在 t 中。重复这个操作,直到 s 中的字符全部被遍历,且全部在 t 中可以找到,此时返回 true

/**
 * @param {string} s
 * @param {string} t
 * @return {boolean}
 */
var isSubsequence = function(s, t) {
  // start 指示从哪个位置开始搜寻当前遍历的s中的字符,这个很关键,因为要保证s是t的子序列
  let start = -1;
  // 开始遍历s字符
  for(let i = 0; i < s.length; i++) {
    // 开始从指定的位置搜寻当前遍历的字符,并将其在t中的索引记录在start中
    start = t.indexOf(s[i], start + 1);
    // 如果发现t中没有当前字符,则直接返回false,否则的话从start开始搜寻下一个字符
    if(start === -1) {
      return false;
    }
  }
  // 如果搜寻一遍后发现,s中的字符在t中都能找到,且是按序找到的,则说明s是t的自序列
  return true;
};


提交记录
17 / 17 个通过测试用例
状态:通过
执行用时:68 ms, 在所有 JavaScript 提交中击败了79.15%的用户
内存消耗:37.3 MB, 在所有 JavaScript 提交中击败了98.50%的用户
时间:2021/10/22 22:33

注意,上面的 start 是保证 s 中字符的相对位置和 t 中字符相对位置保持一致的关键点。

我看到官方题解里还有双指针的解法,我觉得那种解法和我的这种解法其实都差不多吧,就不再展示了。

我的题解2(动态规划)

更新:2021年10月22日23:43:30

洗了个澡,休息了一下继续~

我看到【相关标签】里提到了“动态规划”。而且说到子序列,我想起了之前做过的一个子序列且运用动态规划的思路的题目:

参考:【算法-LeetCode】1143. 最长公共子序列(动态规划;滚动数组;通用的空间优化)_赖念安的博客-CSDN博客

仔细思考后,我发现这题完全可以照搬上面这题的思路,只不过最后的返回值不同而已……

动态规划的具体思路解析可以看上面的那篇博客。

通过动态规划获取 st 的最长公共子序列长度后,将该长度与 s 的长度做比较,如果两者相等,那么说明 st 的子序列,所以返回 true;如果不相等的话,那么就说明 t 中没有包含 s 中的全部字符,此时返回 false

/**
 * @param {string} s
 * @param {string} t
 * @return {boolean}
 */
var isSubsequence = function(s, t) {
  let dp = Array.from({length: 2}).map(
    () => Array.from({length: s.length+1}).fill(0)
  );
  for(let i = 1; i < t.length+1; i++) {
    for(let j = 1; j < s.length+1; j++) {
      if(t[i-1] !== s[j-1]) {
        dp[1][j] = Math.max(dp[0][j], dp[1][j-1]);
      } else {
        dp[1][j] = dp[0][j-1] + 1;
      }
    }
    dp[0] = [...dp[1]];
  }
  // 此处是与【算法-LeetCode】1143. 最长公共子序列】唯一不同的地方
  return dp[1][s.length] === s.length ? true : false;
};

提交记录
17 / 17 个通过测试用例
状态:通过
执行用时:80 ms, 在所有 JavaScript 提交中击败了26.36%的用户
内存消耗:40.2 MB, 在所有 JavaScript 提交中击败了8.51%的用户
时间:2021/10/22 23:41

但是,上面的这个动态规划思路和【官方题解】的动态规划的思路不大一样。感觉那种思路还挺绕的……我觉得这个题为啥要用动态规划来做呢?一开始是真的想不到这个方向……

官方题解

更新:2021年7月29日18:43:21

因为我考虑到著作权归属问题,所以【官方题解】部分我不再粘贴具体的代码了,可到下方的链接中查看。

更新:2021年10月22日22:37:56

参考:判断子序列 - 判断子序列 - 力扣(LeetCode)

【更新结束】

有关参考

更新:2021年10月22日23:45:59
参考:【算法-LeetCode】1143. 最长公共子序列(动态规划;滚动数组;通用的空间优化)_赖念安的博客-CSDN博客
参考:【算法-LeetCode】53. 最大子序和(动态规划初体验)_赖念安的博客-CSDN博客

你可能感兴趣的:(LeetCode,算法,leetcode,动态规划,indexOf,javascript)