题目描述
给定三个字符串 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 + ...
提示:a + b
意味着字符串 a
和 b
连接。
示例1:
输入:s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac" |
示例2:
输入:s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc" |
解题思路
深度优先遍历
借助于辅助函数 isInterleave(String s1, String s2, String s3, int st1, int st2, int st3)
,判断 s1[st1:]
与 s2[st2:]
是否能形成 s3[st3:]
所示的交错字符串,因此原问题变为了 isInterleave(s1, s2, s3, 0, 0, 0)
。
对于 isInterleave(s1, s2, s3, st1, st2, st3)
的求解则可以递归调用自己,如果 s1[st1] == s2[st2] == s3[st3]
,说明 s1[st1]
和 s2[st2]
都可作为 s3[st3]
,此时便衍生为两种可能, s1[st1]
作为 s3[st3]
产生的结果为 isInterleave(s1, s2, s3, st1 + 1, st2, st3 + 1)
,或者 s2[st2]
作为 s3[st3]
产生的结果为 isInterleave(s1, s2, s3, st1, st2 + 1, st3 + 1)
,无论这两种可能哪一种为真,那么最终的结果就是真,如果都为假,就说明无法形成指定的交错字符串。
如果只有 s1[st1] == s3[st3]
,则递归调用 isInterleave(s1, s2, s3, st1 + 1, st3 + 1)
,如果只有 s2[st2] == s3[st3]
,则递归调用 isInterleave(s1, s2, s3, st1, st2 + 1, st3 + 1)
,否则 s1
与 s2
的首字符都与 s3
的首字符不同,则无论怎样都无法形成 s3
所示的交错字符,返回结果为 false
。
public boolean isInterleave(String s1, String s2, String s3) { |
此方法运行超时,未能通过最后一个测试用例。
动态规划
我们定义一个函数 dp[i, j]
表示 s1
的前 i
个字符与 s2
的前 j
个字符能否形成 s3
前 i + j
个字符所示交错字符串,定义 l1
为 s1
的长度,l2
为 s2
的长度,则原问题结果为求解 dp[l1][l2]
的值。
而 dp[i][j]
的取值可如下确定
- 如果
s1[i - 1] == s3[i + j - 1]
并且dp[i - 1][j]
为true
,则dp[i][j]
为true
- 如果
s2[j - 1] == s3[i + j - 1]
并且dp[i][j - 1]
为true
,则dp[i][j]
为true
- 否则
dp[i][j]
为false
注:编程中字符的索引从
0
开始,所以i - 1
表示第i
个字符,另s1
等不是数组,为了讲解方便使用[i]
的形式进行索引,实际编程应使用特定的 API 访问,如charAt(i)
。
所以 dp[i][j]
的递归公式为
dp[i][j] = (s1[i - 1] == s3[i + j - 1] && dp[i - 1][j]) || (s2[j - 1] == s3[i + j - 1] && dp[i][j - 1]) |
具体编程时还应考虑 (i - 1)
与 (j - 1)
是否越界,所以上面的程序可以修改为
if (i > 0) { |
完整程序如下
public boolean isInterleave(String s1, String s2, String s3) { |
优化:空间压缩
考虑到 dp[i][j]
每次只用了 dp[i - 1]
这上一层的数据,因此我们没有必要使用一个二维数组保存状态,使用一个一维数组即可,修改上面的程序如下
public boolean isInterleave(String s1, String s2, String s3) { |