题目描述:
给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的。
示例 1:
输入: s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbcbcac”
输出: true
示例 2:
输入: s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbbaccc”
输出: false
分析(来自官方解答)
在没有看解答之前,我还想着用最古老的方法:数对应字符个数是否一样来判断s3能否用s1和s2交错,明显不是这道题的目的,而且时间肯定会超时,所以直接看了官方解答。
DP动态规划
- 定义数组
f(i, j)
为s1
的前i
个字符和s2
的前j
个字符能否交错组成s3
的前i+j
个元素。C++里非0的都为true,0为false。 - 能交错的话证明s3里的元素是来自s1和s2,分两种情况:
- s1的第i个元素和s3的第i+j个元素相等:
s1的前i个元素和s2的前j个元素能否交错组成s3的前i+j个元素,取决于s1的前i-1个元素和s2的前j个元素能否交错组成s3的前i-1+j
个元素。
此时f(i,j)
的真假取决于f(i-1,j)
- s2的第j个元素和s3的第i+j个元素相等:
s2的前j个元素和s1的前i个元素能否交错组成s3的前i+j个元素,取决于s2的前j-1个元素和s1的前i个元素能否交错组成s3的前i+j-1
个元素。
此时f(i,j)
的真假取决于f(i,j-1)
得到动态规划DP的核心方程为:
注意不要搞混:这里的f(i-1,j)
中的i-1和j指的是前i-1和前j个元素;
而s1(i-1)==s3(p)
里的i-1与p指的就是第i和第p=i+j个元素(因为数组的话下标是从0开始的);
//DP =
p=i+j;
f(i, j) = (f(i-1,j) && s1(i-1)==s3(p)) || (f(i,j-1) && s2(j-1)==s3(p));
代码1:
class Solution {
public:
bool isInterleave(string s1, string s2, string s3)
{
//定以长度以及结果(类似于布尔数组吧,false就是0,非0就是true)
int m = s1.size(), n = s2.size(), t = s3.size();
auto f = vector<vector<int>> (m+1, vector<int>(n+1, false));//auto可以在声明变量的时候根据变量初始值的类型自动为此变量选择匹配的类型
//特殊处理
if(m+n != t)
retuen false;
//处理
f[0][0] = true;
for(int i=0;i<m+1;i++)
{
for(int j=0;j<n+1;j++)
{
int p = i+j-1;
if(i>0)//包含了j=0的情况
{
f[i][j] |= (f[i-1][j] && (s1[i-1]==s3[p]));
}
if(j>0)//包含了i=0的情况
{
f[i][j] |= (f[i][j-1] && (s2[j-1]==s3[p]));
}
}
}
return f[m][n];
}
};
定义二维数组的方法二:
vector<vector<int>> f(m+1);
for(int i=0;i<m+1;i++)
{
f[i] = vector<int> (n+1, i);
}
代码2(动态规划路径问题解法):
问题:有点不懂为何要设置这个边界
关于这个边界值:有边界值才能有与的条件,类似于代码1。
下表从1开始是因为:f[i,j]表示s1前i字符能与s2前j字符组成s3前i+j个字符
思路分析:
边界1:路径左上角的值f[0][0]=true
;
边界2:当i=0时,f[i][j]=f[0][j] = s2的n个值是否与s3的前n个值相等
;遇到false后面可以直接省略
边界3:当j=0时,f[i][j]=f[i][0] = s1的m个值是否与s3的前m个值相等
;遇到false后面可以直接省略
f[i,j]表示s1前i字符能否与s2前j字符组成s3前i+j个字符,也代表是否存在这么一个路径使得s1和s2能交叉拼接成s3。
bool isInterleave(string s1, string s2, string s3)
{
int m = s1.size(), n = s2.size(), t=s3.size();
//f[i,j]表示s1前i字符能与s2前j字符组成s3前i+j个字符;
auto f = vector<vector<int>> (m+1, vector<int>(n+1, false));
//特殊处理
if(m+n != t)
return false;
//边界1
f[0][0] = true;
//边界2 :不相符直接终止
for(int i=1;i<m+1 && s1[i-1] == s3[i-1];i++)
f[i][0] = true;
//边界3:不相符直接终止
for(int j=1;j<n+1;j++)
f[0][j] = true;
//处理
for(int i=1;i<m+1;i++)
{
for(int j=1;j<n+1;j++)
{
int p = i+j-1;
f[i][j] = (f[i-1][j] && (s1[i-1] == s3[p])) || (f[i][j-1] && (s2[j-1] == s3[p]));
}
}
return f[m][n];
}