题目:
Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2.
For example,
Given:
s1 ="aabcc",
s2 ="dbbca",
When s3 ="aadbbcbcac", return true.
When s3 ="aadbbbaccc", return false.
题意:
判断s3是否由是s1,s2交叉得到。 这里的交叉是指顺序交叉,不能跳跃性交叉。
解题思路:
一般遇到字符串的子序列或者匹配问题,那么一般可以使用动态规划自底向上进行求解。这题与那个Word Break有点类似。
动态规划一般有两个难点,第一个难点就是边界的确认。
我们可以把例子的那两个s1,s2进行字符拆分。如下图所示(这图我是摘自http://www.cnblogs.com/grandyang/p/4298664.html)
Ø d b b c a Ø T F F F F F a T F F F F F a T T T T T F b F T T F T F c F F T T T T c F F F T F T
这里我们可以使用一个dp[i][j]二维boolean数组来表示,i+j 表示s3是否由s1的前i个字符,s2的前j个字符构成。
我们可以看到,当s1,s2都为空串时,此时dp[0][0] = true,可以理解为s1中取0个字符 s2中取0个字符 匹配s3从0开始的0个字符 那么肯定匹配true
上边界可以表示为:dp[0][i] = dp[0][i-1] && (s2.charAt(i-1) == s3.charAt(i-1)); 这里可以理解为从s1取0个字符,s2取i个字符去和s3 i+0个字符进行匹配,同时根据上一个位置的值来进行赋值(这里我觉得也体现了动态规划自底向上的一种思想,上一个的值总会影响下一个位置的值)
同理左边界可以表示为:dp[i][0] = dp[i-1][0] && (s1.charAt(i-1) == s3.charAt(i-1)); 这里可以理解为s2中取0个,s1中取i个 去和s3中0+i 个匹配,同时也根据上一个位置的值来进行赋值
这里边界就已经确定好了,下面就需要确认动态规划的公式了
这里的公式为:
dp[i][j] = (dp[i-1][j] && (s1.charAt(i-1) == s3.charAt(i-1+j))) || (dp[i][j-1] && (s2.charAt(j-1) == s3.charAt(j-1+i)));
这里面有两个动态转移方程
第一个是dp[i-1][j] && (s1.charAt(i-1) == s3.charAt(i-1+j)),这里可以理解为
dp[i-1][j]表示若s3的前i+j-1个字符能够由s1前i-1个字符和s2的前j个字符交织而成,那么只需要s1的第i个字符与s3的第i+j个字符相等(charAt索引从0开始),那么dp[i][j]=true;
第二个转移方程是:dp[i][j-1] && (s2.charAt(j-1) == s3.charAt(j-1+i)),这里也可以这么理解:
dp[i-1][j]表示若s3的前i+j-1个字符能够由s1前i个字符和s2的前j-1个字符交织而成,那么只需要s2的第j个字符与s3的第i+j个字符相等(charAt索引从0开始),那么dp[i][j]=true;
只要这两个转移方程有一个为true,那么就可以判断s3是由s1,s2交叉构成
下面给出代码:
public static boolean isInterleave(String s1, String s2, String s3) {
int length1 = s1.length();
int length2 = s2.length();
int length3 = s3.length();
if(length1 + length2 != length3) {
return false;
}
boolean[][] dp = new boolean[length1+1][length2+1];
//初始化边界
dp[0][0] = true;
//初始化第一行
for(int i = 1;i <= length2;i++) {
dp[0][i] = dp[0][i-1] && (s2.charAt(i-1) == s3.charAt(i-1));
}
//初始化第一列
for(int i = 1;i <= length1;i++) {
dp[i][0] = dp[i-1][0] && (s1.charAt(i-1) == s3.charAt(i-1));
}
//dp
for(int i = 1;i <= length1;i++) {
for(int j = 1; j <= length2; j++) {
dp[i][j] = (dp[i-1][j] && (s1.charAt(i-1) == s3.charAt(i-1+j))) || (dp[i][j-1] && (s2.charAt(j-1) == s3.charAt(j-1+i)));
}
}
return dp[length1][length2];
}