题目:给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的。
示例 1:
输入: s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbcbcac”
输出: true
示例 2:
输入: s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbbaccc”
输出: false
首先这道题我们需要理解什么是"交错",我把s1和s2中的字符串在s3中用不同颜色表示出来,马上豁然开朗
s1用红色标注,s2用黄色标注:
示例1(当然组合不止这一种情况):
我们发现在s3中可以依序找到完整的s1以及s2的部分。
因此输出结果是true
然而示例2并不能像这样成功找到拼出s3的s1以及s2部分。
因此输出结果是false
很多人的第一想法可能是利用双指针法去解决问题。
给s1以及s2分别设置两个指针,从头指向尾,按序判断它们的字母是否与s3中的字母相等,如果s3中的字母全部能依序找到s1以及s2中的归宿,那么输出结果是true,否则结果是false。
乍一看,这个思路很对,然而实际实现后发现,对于示例1,给出的答案是false
我分析了一下原因,发现双指针法对于处理这种有很多重复字母的情况表现并不好,它没有办法判断目前这个同时满足条件的字母到底属于s1还是s2,如果判断错误,就会导致之后的字母无法正确组合出想要的结果,进而给出错误答案。
所以我们要另辟蹊径,利用“动态规划”。
第一件事是判断字母长度 ,如果|s1|+|s2|≠|s3|
那么返回false
(字母长度都不一样,怎么可能得出正确结果呢)
如果长度相等,我们可以进行下一步操作。
定义:f[i][j]
表示 s1 的前 i 个字母和 s2 的前 j 个字母是否能组成 s3的前 i+j 个字母,是一个布尔类型变量。
同时f[i][j]
的值取决于f[i-1][j]
的值,如果 s1 的第 i 个字母与 s3 的第 i+j 个字母相等,可以得出等式:
f [ i ][ j ] = f [ i-1 ][ j ] and (s1 [ i-1 ]==s3 [ i+j-1 ])
同理,f[i][j]
取决于f[i][j-1]
的值,如果 s2 的第 j 个字母与 s3 的第 i+j 个字母相等,可以得出等式:
f [ i ][ j ] = f [ i ][ j-1 ] and (s2 [ j-1 ]==s3 [ i+j-1 ])
综上,我们可以得出f[i][j]
的总关系式为:
f[i][j] = {f[i-1][j] and (s1[i-1]==s3[i+j-1])} or {f[i][j-1] and (s2[j-1]==s3[i+j-1])}
我们还要设置边界条件:f [0][0] = true
现在就可以用代码去实现了!
public class answer5 {
public boolean isInterleave(String s1, String s2, String s3){
int a = s1.length();
int b = s2.length();
int c = s3.length();
//f二维数组[]中的数表示前多少个字母
//s1,s2最多分别为前a和前b个字母,因此f定义的大小分别为a+1和b+1
boolean[][] f = new boolean[a+1][b+1];
f[0][0] = true;//边界条件
if(a+b != c) return false;//字母长度不一样即可结束判断
//开始迭代计算每种f的情况
for (int i = 0;i<=a;i++){
for(int j = 0;j<=b;j++){
int p = i+j-1;//p代指s3中目前考虑到的最后一个字符所在位置
if(i>0) f[i][j] = f[i-1][j] && (s1.charAt(i-1)==s3.charAt(p));
if(j>0) f[i][j] = f[i][j] || (f[i][j-1] && (s2.charAt(j-1)==s3.charAt(p)));
//j这行多一个判决条件f[i][j]是因为要考虑到上一行 i 中判决的结果
}
}
return f[a][b];
}
}