题目
Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2.
具体例子见原题那里。
分析
嗯这个问题是求s3是不是s1和s2的字串按顺序组成的字符串。关于字符串的字串匹配等问题一般都使用动态规划的。然后动态规划的套路是先初始化一个二维数组,然后更新第一行和第一列,然后再找递推公式来填中间部分。这道题有点绕,我看别人的博客也是看了好几个才理解这个递推是怎么回事。
过程就是这个样子:比如说输入s1 = "abc" s2 = "bdc" s3 = "abbcdc"。然后像这样初始化一个二维数组:
0 b d c
0 T F F F
a T T F F
b T T F F
c F T T T
讲解一下过程:竖着是s1,横着是s2。dp[i][j]表示用s1的前i位和s2的前j位是否可以组合成s3的前(i+j)位。如果存在一条从左上角到右下角的T路径,说明是可以匹配的。如果s1和s2的长度都是0,显然如果s3的长度为0时,可以匹配。所以dp[0][0] = T。然后第一行和第一列也十分好填。因为其中一个串是空串,则只要看看s3的前n位是否和非空串的前n位相同即可。 如果已经产生了不匹配,那剩下的连看都不用看了,肯定全是F。比如填写第一行:此时s1是空串,s3的第一位是a,而s2的第一位是b,所以肯定s1的前0位和s2的前1位不可以组合成s3的前(0+1)位。所以填F。然后第一行后面的部分肯定也全是F了。第一列的填法同理。
填好第一行和第一列之后,剩下的就是递推了。这里的窍门是:如果当前位置(i,j)的上方和左方都是F,说明s1的前i-1位和s2的前j位不可以组合成s3的前(i-1+j)位且s1的前i位和s2的前j-1位不可以组合成s3的前(i+j-1)位,那就表示凉了,用s1的前i位和s2的前j位肯定没法匹配s3的前i+j位了。
只要该位置左边或者上面有一个是T,说明有希望:不妨看咱们的例子中的位置(1,2),它左边(1,1)是T,然后这个T在自己左边,所以要看s2的第j-1个字符是否和s3的第i+j-1个字符对应。为什么我们要用s2来比对为不是s1呢?因为上一个T在自己左边,说明我们正在试图向右创建T路径。向右走是用s2的字符来匹配s3。相同的可以看一下(3,1),它上面(2,1)是个T,说明我们试图向下创造T路径,向下的话是用s1的字符来匹配s3,所以检测s1[3-1]是否等于s3[3+1-1],发现匹配所以填T.
就这样一步一步填就ok了。整个过程可以看作在试图创建T路径:能不能往右走呢?能不能往下走呢?即已知s1的前i个字符与s2的前j个字符可以组成s3的前i+j个字符,那s3的前i+j+1个字符可以匹配吗?这就要看s3[i+j+1]是否与s1[i+1]或者s2[j+1]匹配了。
嗯不得不说这个题很绕。
代码
class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
int row = s1.size();
int col = s2.size();
int len = s3.size();
if(row+col!=len) return false;
bool dp[row+1][col+1];
dp[0][0] = true;
for(int i=1;i<=row;i++)
{
if(dp[i-1][0])
{
if(s1[i-1]!=s3[i-1]) dp[i][0] = false;
else dp[i][0] = true;
}
else dp[i][0] = false;
}
for(int j=1;j<=col;j++)
{
if(dp[0][j-1])
{
if(s2[j-1]!=s3[j-1]) dp[0][j] = false;
else dp[0][j] = true;
}
else dp[0][j] = false;
}
for(int i=1;i<=row;i++)
for(int j=1;j<=col;j++)
{
dp[i][j] = (dp[i - 1][j] && s1[i - 1] == s3[i - 1 + j]) || (dp[i][j - 1] && s2[j - 1] == s3[j - 1 + i]);
}
return dp[row][col];
}
};