leetcode97. Interleaving String
题目描述如下:
Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2.
Example 1:
Input: s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbcbcac”
Output: true
Example 2:
Input: s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbbaccc”
Output: false
题目非常的简单粗暴,就是两个字符串交叉能不能得到第三个字符串,一开始最暴力的想法当然是所有的插入方法都试一遍,如果不考虑效率的话这样当然是可行的,但是这样的复杂度是组合数,已经接近阶乘级别,所以我们需要对其进行优化,所以在这个过程中有哪些搜索的情况是冗余的呢?首先,如果目标字符串在到达某个位置时用前两个字符串的前缀无论怎么样都组合不出来时,后面的部分自然就不用考虑了,所以这样我们就可以借助这个特点,把匹配字符串变成匹配字符串的前缀,这样问题就变成了子问题了,所以就可以很自然而然的想到可以用动归。
确认了动归这个思路后,因为扫描的顺序肯定是字符串从前往后扫描然后匹配,所以可以用一个二维数组result[i][j]存储字符串s1的前i个字符和字符串s2的前j的字符交叉一下能不能匹配s3的一个前缀,而如果s1[0:i-1]和s2[0:j-1]可以匹配,那么考虑匹配的最后一个字符,它要么是s1中的第i个,要么是s2中的第j个,并且除掉这个字符剩余的前缀依然是匹配的,如果是s1提供最后一个,那么就说明result[i-1][j]为真,如果s2提供最后一个,就说明result[i][j-1]为真。所以就可以写出状态转移方程:
result[i][j] = (result[i-1][j] && s1[i-1] == s3[i+j-1]) || (result[i][j-1] && s2[j-1] == s3[i+j-1])
代码实现如下:
class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
int l1 = s1.length();
int l2 = s2.length();
int l3 = s3.length();
if(l1 + l2 != l3){
return false;
}
bool result[l1+1][l2+1];
for(int i = 0; i <= l1; i++){
for(int j = 0; j <= l2; j++){
result[i][j] = (i-1>=0 && result[i-1][j] && s3[i+j-1] == s1[i-1]) || (j-1>=0 && result[i][j-1] && s3[i+j-1] == s2[j-1]);
result[0][0] = true; //这里其实也可以先初始化然后判断ij是否为0来实现,不过这样就省了一个判断了
}
}
return result[l1][l2];
}
};
代码复杂度为O(L1*L2),L1为字符串s1长度,L2为字符串s2长度