题目地址:
https://www.lintcode.com/problem/interleaving-string/description
给定三个字符串 s 1 s_1 s1, s 2 s_2 s2和 s 3 s_3 s3,问 s 1 s_1 s1和 s 2 s_2 s2是否可以”交织“在一起得到 s 3 s_3 s3。交织的意思是,保持 s 1 s_1 s1和 s 2 s_2 s2里面字符相对顺序不变的情况下,将它们的字符拆开然后重新组合在一起,能否得到 s 3 s_3 s3。
思路是动态规划。设 f [ i ] [ j ] f[i][j] f[i][j]表示 s 1 s_1 s1的前 i i i个字符和 s 2 s_2 s2的前 j j j个字符交织在一起是否可以得到 s 3 s_3 s3的前 i + j i+j i+j个字符。这里的 i i i和 j j j都从 1 1 1开始计数,前 0 0 0个字符视为空串。
很显然,
f
[
0
]
[
0
]
=
t
r
u
e
f[0][0]=true
f[0][0]=true。接下来,
如果
s
1
[
i
−
1
]
=
s
3
[
i
+
j
−
1
]
s_1[i-1]=s_3[i+j-1]
s1[i−1]=s3[i+j−1],那么如果
f
[
i
−
1
]
[
j
]
=
t
r
u
e
f[i-1][j]=true
f[i−1][j]=true的话
f
[
i
]
[
j
]
f[i][j]
f[i][j]就等于
t
r
u
e
true
true;
如果
s
2
[
j
−
1
]
=
s
3
[
i
+
j
−
1
]
s_2[j-1]=s_3[i+j-1]
s2[j−1]=s3[i+j−1],那么如果
f
[
i
]
[
j
−
1
]
=
t
r
u
e
f[i][j-1]=true
f[i][j−1]=true的话
f
[
i
]
[
j
]
f[i][j]
f[i][j]就等于
t
r
u
e
true
true;
如果
s
1
[
i
−
1
]
=
s
3
[
i
+
j
−
1
]
s_1[i-1]=s_3[i+j-1]
s1[i−1]=s3[i+j−1]和
s
2
[
j
−
1
]
=
s
3
[
i
+
j
−
1
]
s_2[j-1]=s_3[i+j-1]
s2[j−1]=s3[i+j−1]都不成立,
f
[
i
]
[
j
]
=
f
a
l
s
e
f[i][j]=false
f[i][j]=false。
代码如下:
public class Solution {
/**
* @param s1: A string
* @param s2: A string
* @param s3: A string
* @return: Determine whether s3 is formed by interleaving of s1 and s2
*/
public boolean isInterleave(String s1, String s2, String s3) {
// write your code here
// 如果字符串的长度不符合要求则直接返回false
if (s1.length() + s2.length() != s3.length()) {
return false;
}
int l1 = s1.length(), l2 = s2.length();
boolean[][] dp = new boolean[l1 + 1][l2 + 1];
for (int i = 0; i <= l1; i++) {
for (int j = 0; j <= l2; j++) {
if (i == 0 && j == 0) {
dp[i][j] = true;
} else {
// 先初始化dp[i][j]为false,接下来根据两种可能性尝试去更新它
dp[i][j] = false;
if (i > 0 && s1.charAt(i - 1) == s3.charAt(i + j - 1)) {
dp[i][j] |= dp[i - 1][j];
}
if (j > 0 && s2.charAt(j - 1) == s3.charAt(i + j - 1)) {
dp[i][j] |= dp[i][j - 1];
}
}
}
}
return dp[l1][l2];
}
}
时空复杂度 O ( m n ) O(mn) O(mn), m m m和 n n n分别是 s 1 s1 s1和 s 2 s2 s2的长度。
可以使用空间优化的方式使得空间复杂度降为 O ( n ) O(n) O(n)。我们注意到, f [ i ] [ j ] f[i][j] f[i][j]只取决于其正上方和正左方的数据,所以我们更新的时候 j j j需要从小到大更新,保证依赖正左方的数据的时候左方数据已经被更新。代码如下:
public class Solution {
/**
* @param s1: A string
* @param s2: A string
* @param s3: A string
* @return: Determine whether s3 is formed by interleaving of s1 and s2
*/
public boolean isInterleave(String s1, String s2, String s3) {
// write your code here
if (s1.length() + s2.length() != s3.length()) {
return false;
}
int l1 = s1.length(), l2 = s2.length();
boolean[] dp = new boolean[l2 + 1];
for (int i = 0; i <= l1; i++) {
for (int j = 0; j <= l2; j++) {
if (i == 0 && j == 0) {
dp[0] = true;
} else {
// 原先的逻辑是,先初始化dp[i][j]为false,然后逐次尝试更新它
// 转变为一维后,逻辑变为,第一个if的条件如果成立,dp[j]不变,否则dp[j]赋值为false,接下来再根据第二个if进行更新。
// 这里不能一上来就把dp[j]初始化为false,因为我们需要用到上一行的dp[j]的值,所以必须要先用掉这个信息。
if (!(i > 0 && s1.charAt(i - 1) == s3.charAt(i + j - 1))) {
dp[j] = false;
}
if (j > 0 && s2.charAt(j - 1) == s3.charAt(i + j - 1)) {
dp[j] |= dp[j - 1];
}
}
}
}
return dp[l2];
}
}
时间复杂度不变,空间 O ( n ) O(n) O(n)。