Java数据结构与算法之交错字符串97(DP)
给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的。
思路分析
当我看到这道题的第一想法是用三指针的方法解决:index1
指向s1
,index2
指向s2
,index3
指向s3
。按照顺序判断s3
的字符是否与s1、s2
的字符一致,即先与s1
判断,不一致再与s2
判断;若都不一样则输出false
。
但这个解法在随后的测试中暴露了问题,与s1、s2比较是存在先后顺序的,所以如果s2出现s3的前部,则会出现问题。
比如"aa" "ab" "abaa"
这个案例中,结果显示false
,明显是错误的。
所以基于以上的问题,我们重新考虑问题。使用动态规划解题。我们依旧使用上面的案例进行解释。
-
首先考虑边界条件,
s1.length()+s2.length() != s3.length()
这种情况那么必然输入false; -
s1.length()+s2.length() == s3.length()
时,我们可以利用DP思想解决这个问题; -
定义
dp[i][j]
为s1
的前i
个元素和s2
的前j
个元素是否可以组合成s3
的前i+j
个元素; -
因此想要判断
dp[i][j]
位置的状态需要知道dp[i-1][j]
或dp[i][j-1]
的状态,换言之,只有dp[i-1][j]
或dp[i][j-1]
为true
才会有dp[i][j]
的进一步判断; -
dp[i-1][j]
代表s1
的前i-1
个元素和s2
的前j
个元素的组合状态。那么s1
的第i-1
元素呢,它是否可以组合为s3
的第i+j-1
元素呢? -
所以
dp[i-1][j]
与s1.charAt(i-1) == s3.charAt(i+j-1)
才是dp[i][j]
的真实状态;反之dp[i][j-1]
亦然; -
DP的初始状态
dp[0][0]
必然为true
。
所以我们可以推导出动态规划方程为:dp[i][j] = (dp[i-1][j] && s1.charAt(i-1) == s3.charAt(i+j-1)) || (dp[i][j-1] && s2.charAt(j-1) == s3.charAt(i+j-1))
代码实现
class Solution {
public boolean isInterleave(String s1, String s2, String s3) {
int index1 = s1.length(), index2 = s2.length();
if(s3.length() != index1+index2) {
return false;
}
boolean[][] dp = new boolean[index1+1][index2+1];
dp[0][0] = true;
// 为什么这儿要从0开始遍历呢?这就是三指针的问题所在。
// 当s3的开头字符是s2,这样在i=0的情况下就可以先搜索s2字符串了。
for(int i = 0; i <= index1; i++) {
for(int j = 0; j <= index2; j++) {
int p = i+j-1;
if(i > 0) {
// 这儿不加dp[i][j]的原因是先判断if(i > 0)
// 如果i<0,那么dp[i][j]就是默认值false,不影响下面的执行
dp[i][j] = dp[i-1][j] && s1.charAt(i-1) == s3.charAt(p);
}
if(j > 0) {
// 这儿为什么要加dp[i][j]?
// 如果在if(i > 0)中已经判断为true了,则不用再判断了
// 如果在if(i > 0)中已经判断为false了,则继续判断在if(j > 0)条件下是否满足
dp[i][j] = dp[i][j] || dp[i][j-1] && s2.charAt(j-1) == s3.charAt(p);
}
}
}
return dp[index1][index2];
}
}
执行结果