判断子序列

双指针

  1. 解题思路:

    • 设置双指针 i , j 分别指向字符串 s , t 的首个字符,遍历字符串 t :
      • 当 s[i] == t[j] 时,代表匹配成功,此时同时 i++ , j++ ;
      • 若 i 已走过 s 尾部,代表 s 是 t 的子序列,此时应提前返回 true ;
      • 当 s[i] != t[j] 时,代表匹配失败,此时仅 j++ ;
    • 若遍历完字符串 t 后,字符串 s 仍未遍历完,代表 s 不是 t 的子序列,此时返回 false;
  2. 复杂度:

    • 时间复杂度: O(N),其中 N 为字符串 t 的长度,最差情况下需完整遍历 t ;
    • 空间复杂度: O(1),i , j 变量使用常数大小空间;
  3. 代码实现:

    var isSubsequence = function (s, t) {
      if (s.length == 0) return true;
    
      for (let i = 0, j = 0; j < t.length; j++) {
        if (s[i] === t[j]) {
          i++;
          if (i == s.length)
            // 若已经遍历完 s ,则提前返回 true
            return true;
        }
      }
    
      return false;
    }
    

动态规划

  1. 解题思路:

    • 状态表示: 定义 dp[i][j] 表示字符串 s 的 [1,i] 区间和字符串 t 的 [1,j] 区间的最长公共子序列长度(下标从1开始);
    • 状态计算:可以根据 s[i] 和 t[j] 的情况,分为两种决策:
      • 若 s[i] == t[j],即两个字符串的最后一位相等,那么就转化成了字符串 s的[1,j-1] 区间和字符串 t 的[1,j-1] 区间的最长公共子序列长度再加上一,即 dp[i][j] = dp[i - 1][j - 1] + 1,(下标从1开始);
      • 若 s[i] != t[j],即两个字符串的最后一位不相等,那么字符串 s的[1,i] 区间在字符串 t 的[1,j] 区间的子序列长度无法延长,因此 dp[i][j] 就会继承 dp[i][j-1],即 dp[i][j] = dp[i][j - 1],( 下标从1开始);如图所示:比较 s[3] 与 t[3],发现’f’不等于’e’,这样 dp[3][3] 无法在原先的基础上延长;因此继承 “acf"与"cf” 的值;即 dp[3][3] = dp[3][2];
    • 状态转移方程为:
      • 当s[i] == t[j]:dp[i][j] = dp[i-1][j-1] + 1;
      • 当s[i] != t[j]​:dp[i][j] = dp[i][j - 1];
    • 初始化:
      • dp[i][0] = dp[0][j] = 0;
      • 空字符串与有长度的字符串的最长公共子序列长度肯定为 0;
    • 实现细节:
      • 前面定义的状态表示 dp 数组和 text 数组下标均是从 1 开始的,而题目给出的 text 数组下标是从 0 开始的;
      • 为了一一对应,在判断 s 和 t数组的最后一位是否相等时,往前错一位,即使用s[i - 1]和 t[j - 1] 来判断;
  2. 代码实现:

    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;
    };
    
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 判断子序列
  2. 2. 双指针
  3. 3. 动态规划