1、背景介绍
给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的。
示例 1:
输入: s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbcbcac”
输出: true
示例 2:
输入: s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbbaccc”
输出: false
2、背景理解
就字面意思理解,其实很简单,这里想讲的是关于它的扩展。
我们可以这样理解,s1和s2中的每个字符都代表一个动作,每个动作会产生一个与之匹配的唯一的状态,那么原背景就可以理解成,在保证s1和s2中各自动作的相对顺序不变的情况下,是否可以达到s3的状态。这样以拓展,原本简单的字符串问题,就可以建模成一个复杂的路径问题。为什么要将之称为路径问题呢,这是我自己起的,就好像每一步都会到一个新的坐标一样。
3、解题思路
有序的状态问题,都可以通过动态规划来解决。
首先,什么问题是有序的状态问题。如果我这一步的状态,是被上一步的动作所影响的,并且每个动作与状态之间存在函数依赖关系,即一个动作对应一个唯一的状态,并且动作执行的相对顺序不能被打破,那这就是有序的状态问题。
动态规划最核心的问题是状态转移方程。
该背景中,倘若给定了二维动态规划状态数组dp,那么数组dp[i][j]表示s3中前i+j个字符(包括第i+j)是否是s1中前i个字符(包括第i)和s2中前j个字符(包括第j)交错组成的,倘若要判断 dp[i][j] 为真(s3中前i+j个字符能否有s1中前i个字符以及s2中前j个字符组成),有两种情形:
1)dp[i-1][j]为真 (s3中前i+j-1个字符能否有s1中前i-1个字符以及s2中前j个字符组成),并且s3中往后移动一个字符即第i+j个字符和s1中往后移动一个字符即第i个字符相等
2)dp[i][j-1]为真 (s3中前i+j-1个字符能否有s1中前i个字符以及s2中前j-1个字符组成),并且s3中往后移动一个字符即第i+j个字符和s2中往后移动一个字符即第j个字符相等
其余情形一律为假。
注意不要把这里的第i个第j个和计算机数组中下标混淆,理解这句话时,不要去想用代码如何实现,单纯理解文字,将方案定好,模型建立成功以后,将之翻译成代码,切不可想方案的时候考虑实现,否则很容易相互影响。
4、代码详解
#include <string>
#include <vector>
using namespace std;
class Solution {
public:
//给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的
bool isInterleave(string s1, string s2, string s3)
{
//如果s3为空,即为真
if (s3.empty())
return true;
int l1 = 0;
int l2 = 0;
int l3 = 0;
//在此处判断s1、s2是否有空的情形
if (!s1.empty()||!s2.empty())
{
if (!s1.empty())
l1 = s1.length();
if (!s2.empty())
l2 = s2.length();
}
//走到此处,s3一定不为空
l3 = s3.length();
//如果数量不相等,直接退出
if (l1 + l2 != l3)
return false;
/*****
初始化dp数组dp[i][j]表示s3中前i+j个字符(包括第i+j)是否是s1中前
i个字符(包括第i)和s2中前j个字符(包括第j)交错组成的
****/
/**这里横纵都多声明一行一列,是因为,如果s1或者s2为空,之后代码
实现的初始化表格中
由于方案中dp[i][j]表示s3中前i+j个字符,那么最后一个字符在数组中
下标未i+j-1,s1中前i个字符最后一个在数组中的下标未i-1,s2为j-1
如果出现了s1或s2为一个字符的情况,声明的dp数组,横纵坐标一定会
有一个为0,
此时如果要表达s3中前1个字符和s1中前1个、s2中前0个,那就是
dp[1][0],但是实际上编译器生成的dp空间下标是从0到0,这时候就会
崩溃,所以多声明了一个空间,来满足方案的表达意义
dp[i][j]
|0|0|1|2|
|1|-|-|-|
|2|-|-|-|
|3|-|-|-|
**/
vector<vector<bool>> dp(l1, vector<bool>(l2,false));
//s2为空时
for (int i = 1; i <= l1; ++i)
{
if (s1[i-1] == s3[i-1])
dp[i][0] = true;
else
break;
}
//s1为空时
for (int j = 1; j <= l2; ++j)
{
if (s2[j-1] == s3[j-1])
dp[0][j] = true;
else
break;
}
for (int32_t i = 1; i <= l1; ++i)
{
for (int32_t j = 1; j <= l2; ++j)
{
//状态转移方程
if ((dp[i - 1][j] && s1[i - 1] == s3[i + j - 1]) || (dp[i][j - 1] && s2[j - 1] == s3[j + i - 1]))
{
dp[i][j] = true;
}
}
}
return dp[l1][l2];
}
};
5、测试用例
建议测试时多测试边缘情况,例如s1、s2、s3各自为空的笛卡尔积结果。