- 题目描述
给定三个字符串 s1、s2、s3,请你帮忙验证 s3 是否是由 s1 和 s2 交错组成的。两个字符串 s 和 t交错的定义与过程如下,其中每个字符串都会被分割成若干 非空 子字符串:
- s = s1 + s2 + … + sn
- t = t1 + t2 + … + tm
- |n - m| <= 1
- 交错 是 s1 + t1 + s2 + t2 + s3 + t3 + … 或者 t1 + s1 + t2 + s2 + t3 + s3 + …
提示:
- 0 <= s1.length, s2.length <= 100
- 0 <= s3.length <= 200
- s1、s2、和 s3 都由小写英文字母组成
来源:LeetCode
- 示例
- 示例 1:
输入:s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbcbcac”
输出:true - 示例 2:
输入:s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbbaccc”
输出:false - 示例 3:
输入:s1 = “”, s2 = “”, s3 = “”
输出:true
- 思路分析
- 动态规划方法。在我意识到自己用的是动态规划之前我的代码已经通过了,就是有点慢,返回去看怎么改进才发现是动态规划。
- 动态规划已经遇到好多题了。假设字符串s1,s2,s3和i,j, i表示s1当前字符的位置,j表示s2当前字符的位置,那么需要考虑的就是s1[i], s2[j], s3[i+j](实际上字符串不能这样用,这里为了简单就这样写了)。定义
m
a
t
c
h
[
i
]
[
j
]
match[i][j]
match[i][j],表示s1[:,i]和s2[:,j]是否能够表示s3[:,i+j](这里借用一下python的字符串截取方法,s[:,i]表示s从头开始到位置i的子串)。那么本题的状态转换方程式:
m a t c h [ i ] [ j ] = { m a t c h [ i − 1 ] [ j ] , i f ( s 1 [ i ] = = s 3 [ i + j ] ) m a t c h [ i ] [ j − 1 ] , i f ( s 2 [ i ] = = s 3 [ i + j ] ) match[i][j]=\left\{ \begin{aligned} match[i-1][j], if(s1[i] == s3[i+j])\\ match[i][j-1], if(s2[i] == s3[i+j]) \end{aligned} \right. match[i][j]={match[i−1][j],if(s1[i]==s3[i+j])match[i][j−1],if(s2[i]==s3[i+j]) - 然后就是注意一下边界判断就行了。动态规划有两种实现方法:自底向上和自顶向下,这里写了两种自底向上的方法:memoization和普通递归。
- JAVA实现
- 自底向上:memoization(通过存储计算过的结果,使递归更高效)
class Solution {
public boolean isInterleave(String s1, String s2, String s3) {
if(s1.length() == 0) return s2.equals(s3);
if(s2.length() == 0) return s1.equals(s3);
if(s1.length()+s2.length()!=s3.length()) return false;
boolean[][] match = new boolean[s1.length()+1][s2.length()+1];
//边界处理
match[0][0] = true;
for(int j=1; j<s2.length()+1; j++) {
if(s2.substring(0,j).equals(s3.substring(0,j))) match[0][j] = match[0][j-1] && true;
else match[0][j] = false;
}
for(int j=1; j<s1.length()+1; j++) {
if(s1.substring(0,j).equals(s3.substring(0,j))) match[j][0] = match[j-1][0] && true;
else match[j][0] = false;
}
//开始递归
for(int i=1; i<s1.length()+1; i++) {
for(int j=1; j<s2.length()+1; j++) {
match[i][j] = (match[i-1][j] && s1.charAt(i-1) == s3.charAt(i+j-1)) || (match[i][j-1] && s2.charAt(j-1) == s3.charAt(i+j-1));
}
}
return match[s1.length()][s2.length()];
}
}
- 自底向上:普通递归,自己调用自己,代码要简单一点
class Solution {
public boolean isInterleave(String s1, String s2, String s3) {
if(s1.length() == 0) return s2.equals(s3);
if(s2.length() == 0) return s1.equals(s3);
if(s1.length()+s2.length()!=s3.length()) return false;
return check(s1, s2, s3, 0, 0, 0);
}
public boolean check(String s1, String s2, String s3, int i, int j, int pos) {
//i、j、pos分别对应当前s1、s2、s3的位置
//base case
if(i==s1.length() && j == s2.length() && pos == s3.length()) return true;
boolean match1 = false, match2 = false;
if(i<s1.length() && s3.charAt(pos) == s1.charAt(i)) {
match1 = check(s1, s2, s3, i+1, j, pos+1);
}
if(match1 == true) return match1;
//这里判断是为了减少递归次数,如果已经发现匹配了,就不考虑其他可能匹配了。
if(j<s2.length() && s3.charAt(pos) == s2.charAt(j)) {
match2 = check(s1, s2, s3, i, j+1, pos+1);
}
if(match2 == true) return match2;
else return false;
}
}