题目
给定三个字符串 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,初始化时,可能需要初始化第一行和第一列的数据。