【动态规划】LeetCode 97.交错字符串-Hard

312 阅读2分钟

题目

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

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

示例 1:

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

示例2:

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

动态规划四步走

1. 定义状态
2. 初始状态
3. 状态转移方程
4. 从dp[]中获取结果

具体到题目

定义状态

定义二维数组dp[i][j]表示s1取前j个字符,s2取前i个字符是否能否构成s3前i+j个字符?

初始状态

dp数组横向长度为s1的长度+1,吗,默认索引0为不使用s1,表示的含义是空字符,即"";宽度同理。 dp[0][0]表示""和""能否构成"",显然成立,因此dp[0][0] = true; 初始化第一行数据,表示的含义是只使用s1的前i个字符能否构成s3的前i个字符;初始化第一列的数据同理

状态转移方程

dp[i][j]可以从i,j位置的上方加上向下走对应的s2的字符,或者从i,j位置的左边加上向右走对应的s1字符, 两者有一个成立即可,因此得到状态转移方程为

dp[i][j] =(dp[i-1][j] && s3[i+j-1] === s2[j]) || (dp[i][j-1] && s3[i+j-1] === s1[j])

从dp[]中获取结果

dp[]的最后一项就是结果

完整代码

// @lc code=start
/**
 * @param {string} s1
 * @param {string} s2
 * @param {string} s3
 * @return {boolean}
 */
var isInterleave = function (s1, s2, s3) {
  const list1 = s1.split('');
  const list2 = s2.split('');
  const list3 = s3.split('');
  const m = list1.length;
  const n = list2.length;
  const p = list3.length;
  if (m + n !== p) {
    return false;
  }
  const dp = new Array(n + 1).fill('').map(() => new Array(m + 1).fill(false));
  dp[0][0] = true;
  for (let i = 1; i < m + 1; i++) {
    dp[0][i] = s1.slice(0, i) === s3.slice(0, i);
  }
  for (let i = 1; i < n + 1; i++) {
    dp[i][0] = s2.slice(0, i) === s3.slice(0, i);
  }
  console.log(dp);
  for (let i = 1; i < n + 1; i++) {
    for (let j = 1; j < m + 1; j++) {
      const top = dp[i - 1][j];
      const left = dp[i][j - 1];
      const toBottomChar = list2[i - 1];
      const toRight = list1[j - 1];
      dp[i][j] =
        (top && toBottomChar === list3[i + j - 1]) ||
        (left && toRight === list3[i + j - 1]);
    }
  }
  console.log(dp);
  return dp[n][m];
};

总结

还是需要慢慢分析,对于二维数组dp,需要理清dp[i][j]和dp[i-1][j]、dp[i][j-1]的关系。

对于二维dp,初始化时,可能需要初始化第一行和第一列的数据。