每日一题--交错字符串

213 阅读1分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。

题目:

给定三个字符串 s1、s2、s3,请你帮忙验证 s3 是否是由 s1 和 s2 交错 组成的。

两个字符串 s 和 t 交错 的定义与过程如下,其中每个字符串都会被分割成若干 非空 子字符串:

s = s1 + s2 + ... + sn t = t1 + t2 + ... + tm |n - m| <= 1 交错 是 s1 + t1 + s2 + t2 + s3 + t3 + ... 或者 t1 + s1 + t2 + s2 + t3 + s3 + ... 提示:a + b 意味着字符串 a 和 b 连接。

 

示例 1: 输入:s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac" 输出:true

示例 2: 输入:s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc" 输出:false

示例 3: 输入:s1 = "", s2 = "", s3 = "" 输出:true

——————————————————————————————————————————

思路:

用三个指针,分别扫描 s1 s2 s3,如下图左例,i、k 指向的字母相同,则选择 s1[i]s1[i] 作为 s3[k]s3[k],i、k 都右移一位,递归剩余子串。

如果 k 最后越界,代表 s3 选完了,成功从 s1 和 s2 中交错选出字母组成自己。

i、j、k 指向相同的字母,选哪个?我们试着选 s1,让指针 i、k 右移,往下递归,如果不能返回 true,说明这个分支得不到 true 的结果,把右移的 i 和 k 撤销回来,改将 j 和 k 右移,向下递归。

下面代码将使用isValid判断这个情况

这就是回溯,试探性地穷举,如果发现当前选择不是问题的解,就撤销当前选择,回到之前的状态,进入另一个分支,继续寻找问题的解

代码


//普通的dfs
const isInterleave = (s1, s2, s3) => {
    if (s1.length + s2.length != s3.length) return false;

    const check = (i, j, k) => { // 检查ijk开始的子串是否满足题目条件
        // k越界,s3扫描完了,返回true
        if (k == s3.length) return true;       

        let isValid = false;                   
        // i指针没有越界,且s1[i]和s3[k]相同
        if (i < s1.length && s1[i] == s3[k]) { 
            isValid = check(i + 1, j, k + 1);    // i、k右移一位,递归考察
        }
        // j 指针没有越界,且s2[i]和s3[k]相同
        if (j < s2.length && s2[j] == s3[k]) { 
            isValid = isValid || check(i, j + 1, k + 1); 
            // 重点: 有可能i、j、k指向相同的字符,尝试 i、k 右移,但已经做过了
            // isValid 就是 check(i + 1, j, k + 1) 的结果
            // 如果它为true,就不用执行 j、k 右移的递归,如果是false,执行递归
        }
        return isValid; // 整个遍历过程都没有返回true,则返回默认的false
    };

    return check(0, 0, 0);
};
//  动态规划
const isInterleave = (s1, s2, s3) => {
  if (s1.length + s2.length != s3.length) return false;
  //创建二维数组
  const memo = new Array(s1.length + 1).fill('').map(item => new Array(s2.length + 1))
//   const memo = new Array(s1.length + 1);
//   for (let i = 0; i < memo.length; i++) {
//     memo[i] = new Array(s2.length + 1);
//   }

  const check = (i, j, k) => {
    if (memo[i][j] != null) return memo[i][j];

    if (k == s3.length) return memo[i][j] = true;

    let isValid = false;

    if (i < s1.length && s1[i] == s3[k]) {
      isValid = check(i + 1, j, k + 1);
    }
    if (j < s2.length && s2[j] == s3[k]) {
      isValid = isValid || check(i, j + 1, k + 1);
    }
    return memo[i][j] = isValid;
  };

  return check(0, 0, 0);
};