字符串
动态规划
题目描述
给定三个字符串 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"
输出: true
示例 2:
输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"
输出: false
示例 3:
输入: s1 = "", s2 = "", s3 = ""
输出: true
提示:
0 <= s1.length, s2.length <= 100
0 <= s3.length <= 200
s1
、s2
、和 s3
都由小写英文字母组成
进阶: 您能否仅使用 O(s2.length)
额外的内存空间来解决它?
解法
方法一:记忆化搜索
我们记字符串 $s_1$ 的长度为 $m$,字符串 $s_2$ 的长度为 $n$,如果 $m + n \neq |s_3|$,那么 $s_3$ 一定不是 $s_1$ 和 $s_2$ 的交错字符串,返回 false
。
接下来,我们设计一个函数 $dfs(i, j)$,表示从 $s_1$ 的第 $i$ 个字符和 $s_2$ 的第 $j$ 个字符开始,能否交错组成 $s_3$ 的剩余部分。那么答案就是 $dfs(0, 0)$。
函数 $dfs(i, j)$ 的计算过程如下:
如果 $i \geq m$ 并且 $j \geq n$,那么说明 $s_1$ 和 $s_2$ 都已经遍历完毕,返回 true
。
如果 $i < m$ 并且 $s_1[i] = s_3[i + j]$,那么说明 $s_1[i]$ 这个字符是 $s_3[i + j]$ 中的一部分,因此递归地调用 $dfs(i + 1, j)$ 判断 $s_1$ 的下一个字符能否和 $s_2$ 的当前字符匹配,如果能匹配成功,就返回 true
。
同理,如果 $j < n$ 并且 $s_2[j] = s_3[i + j]$,那么说明 $s_2[j]$ 这个字符是 $s_3[i + j]$ 中的一部分,因此递归地调用 $dfs(i, j + 1)$ 判断 $s_2$ 的下一个字符能否和 $s_1$ 的当前字符匹配,如果能匹配成功,就返回 true
。
否则,返回 false
。
为了避免重复计算,我们可以使用记忆化搜索。
时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。其中 $m$ 和 $n$ 分别是字符串 $s_1$ 和 $s_2$ 的长度。
Python3 Java C++ Go TypeScript Rust C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 class Solution :
def isInterleave ( self , s1 : str , s2 : str , s3 : str ) -> bool :
@cache
def dfs ( i : int , j : int ) -> bool :
if i >= m and j >= n :
return True
k = i + j
if i < m and s1 [ i ] == s3 [ k ] and dfs ( i + 1 , j ):
return True
if j < n and s2 [ j ] == s3 [ k ] and dfs ( i , j + 1 ):
return True
return False
m , n = len ( s1 ), len ( s2 )
if m + n != len ( s3 ):
return False
return dfs ( 0 , 0 )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 class Solution {
private Map < List < Integer > , Boolean > f = new HashMap <> ();
private String s1 ;
private String s2 ;
private String s3 ;
private int m ;
private int n ;
public boolean isInterleave ( String s1 , String s2 , String s3 ) {
m = s1 . length ();
n = s2 . length ();
if ( m + n != s3 . length ()) {
return false ;
}
this . s1 = s1 ;
this . s2 = s2 ;
this . s3 = s3 ;
return dfs ( 0 , 0 );
}
private boolean dfs ( int i , int j ) {
if ( i >= m && j >= n ) {
return true ;
}
var key = List . of ( i , j );
if ( f . containsKey ( key )) {
return f . get ( key );
}
int k = i + j ;
boolean ans = false ;
if ( i < m && s1 . charAt ( i ) == s3 . charAt ( k ) && dfs ( i + 1 , j )) {
ans = true ;
}
if ( ! ans && j < n && s2 . charAt ( j ) == s3 . charAt ( k ) && dfs ( i , j + 1 )) {
ans = true ;
}
f . put ( key , ans );
return ans ;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 class Solution {
public :
bool isInterleave ( string s1 , string s2 , string s3 ) {
int m = s1 . size (), n = s2 . size ();
if ( m + n != s3 . size ()) {
return false ;
}
vector < vector < int >> f ( m + 1 , vector < int > ( n + 1 , -1 ));
function < bool ( int , int ) > dfs = [ & ]( int i , int j ) {
if ( i >= m && j >= n ) {
return true ;
}
if ( f [ i ][ j ] != -1 ) {
return f [ i ][ j ] == 1 ;
}
f [ i ][ j ] = 0 ;
int k = i + j ;
if ( i < m && s1 [ i ] == s3 [ k ] && dfs ( i + 1 , j )) {
f [ i ][ j ] = 1 ;
}
if ( ! f [ i ][ j ] && j < n && s2 [ j ] == s3 [ k ] && dfs ( i , j + 1 )) {
f [ i ][ j ] = 1 ;
}
return f [ i ][ j ] == 1 ;
};
return dfs ( 0 , 0 );
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 func isInterleave ( s1 string , s2 string , s3 string ) bool {
m , n := len ( s1 ), len ( s2 )
if m + n != len ( s3 ) {
return false
}
f := map [ int ] bool {}
var dfs func ( int , int ) bool
dfs = func ( i , j int ) bool {
if i >= m && j >= n {
return true
}
if v , ok := f [ i * 200 + j ]; ok {
return v
}
k := i + j
f [ i * 200 + j ] = ( i < m && s1 [ i ] == s3 [ k ] && dfs ( i + 1 , j )) || ( j < n && s2 [ j ] == s3 [ k ] && dfs ( i , j + 1 ))
return f [ i * 200 + j ]
}
return dfs ( 0 , 0 )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 function isInterleave ( s1 : string , s2 : string , s3 : string ) : boolean {
const m = s1 . length ;
const n = s2 . length ;
if ( m + n !== s3 . length ) {
return false ;
}
const f : number [][] = new Array ( m + 1 ). fill ( 0 ). map (() => new Array ( n + 1 ). fill ( 0 ));
const dfs = ( i : number , j : number ) : boolean => {
if ( i >= m && j >= n ) {
return true ;
}
if ( f [ i ][ j ]) {
return f [ i ][ j ] === 1 ;
}
f [ i ][ j ] = - 1 ;
if ( i < m && s1 [ i ] === s3 [ i + j ] && dfs ( i + 1 , j )) {
f [ i ][ j ] = 1 ;
}
if ( f [ i ][ j ] === - 1 && j < n && s2 [ j ] === s3 [ i + j ] && dfs ( i , j + 1 )) {
f [ i ][ j ] = 1 ;
}
return f [ i ][ j ] === 1 ;
};
return dfs ( 0 , 0 );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 impl Solution {
#[allow(dead_code)]
pub fn is_interleave ( s1 : String , s2 : String , s3 : String ) -> bool {
let n = s1 . len ();
let m = s2 . len ();
if s1 . len () + s2 . len () != s3 . len () {
return false ;
}
let mut record = vec! [ vec! [ - 1 ; m + 1 ]; n + 1 ];
Self :: dfs (
& mut record ,
n ,
m ,
0 ,
0 ,
& s1 . chars (). collect (),
& s2 . chars (). collect (),
& s3 . chars (). collect (),
)
}
#[allow(dead_code)]
fn dfs (
record : & mut Vec < Vec < i32 >> ,
n : usize ,
m : usize ,
i : usize ,
j : usize ,
s1 : & Vec < char > ,
s2 : & Vec < char > ,
s3 : & Vec < char > ,
) -> bool {
if i >= n && j >= m {
return true ;
}
if record [ i ][ j ] != - 1 {
return record [ i ][ j ] == 1 ;
}
// Set the initial value
record [ i ][ j ] = 0 ;
let k = i + j ;
// Let's try `s1` first
if i < n && s1 [ i ] == s3 [ k ] && Self :: dfs ( record , n , m , i + 1 , j , s1 , s2 , s3 ) {
record [ i ][ j ] = 1 ;
}
// If the first approach does not succeed, let's then try `s2`
if record [ i ][ j ] == 0
&& j < m
&& s2 [ j ] == s3 [ k ]
&& Self :: dfs ( record , n , m , i , j + 1 , s1 , s2 , s3 )
{
record [ i ][ j ] = 1 ;
}
record [ i ][ j ] == 1
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 public class Solution {
private int m ;
private int n ;
private string s1 ;
private string s2 ;
private string s3 ;
private int [,] f ;
public bool IsInterleave ( string s1 , string s2 , string s3 ) {
m = s1 . Length ;
n = s2 . Length ;
if ( m + n != s3 . Length ) {
return false ;
}
this . s1 = s1 ;
this . s2 = s2 ;
this . s3 = s3 ;
f = new int [ m + 1 , n + 1 ];
return dfs ( 0 , 0 );
}
private bool dfs ( int i , int j ) {
if ( i >= m && j >= n ) {
return true ;
}
if ( f [ i , j ] != 0 ) {
return f [ i , j ] == 1 ;
}
f [ i , j ] = - 1 ;
if ( i < m && s1 [ i ] == s3 [ i + j ] && dfs ( i + 1 , j )) {
f [ i , j ] = 1 ;
}
if ( f [ i , j ] == - 1 && j < n && s2 [ j ] == s3 [ i + j ] && dfs ( i , j + 1 )) {
f [ i , j ] = 1 ;
}
return f [ i , j ] == 1 ;
}
}
方法二:动态规划
我们可以将方法一中的记忆化搜索转化为动态规划。
定义 $f[i][j]$ 表示字符串 $s_1$ 的前 $i$ 个字符和字符串 $s_2$ 的前 $j$ 个字符是否能交错组成字符串 $s_3$ 的前 $i + j$ 个字符。在进行状态转移时,我们可以考虑当前字符是由 $s_1$ 的最后一个字符还是 $s_2$ 的最后一个字符得到的,因此有状态转移方程:
$$
f[i][j] = \begin{cases}
f[i - 1][j] & \textit{if } s_1[i - 1] = s_3[i + j - 1] \
\textit{or } f[i][j - 1] & \textit{if } s_2[j - 1] = s_3[i + j - 1] \
\textit{false} & \textit{otherwise}
\end{cases}
$$
其中 $f[0][0] = \textit{true}$ 表示空串是两个空串的交错字符串。
答案即为 $f[m][n]$。
时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。其中 $m$ 和 $n$ 分别是字符串 $s_1$ 和 $s_2$ 的长度。
Python3 Java C++ Go TypeScript C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 class Solution :
def isInterleave ( self , s1 : str , s2 : str , s3 : str ) -> bool :
m , n = len ( s1 ), len ( s2 )
if m + n != len ( s3 ):
return False
f = [[ False ] * ( n + 1 ) for _ in range ( m + 1 )]
f [ 0 ][ 0 ] = True
for i in range ( m + 1 ):
for j in range ( n + 1 ):
k = i + j - 1
if i and s1 [ i - 1 ] == s3 [ k ]:
f [ i ][ j ] = f [ i - 1 ][ j ]
if j and s2 [ j - 1 ] == s3 [ k ]:
f [ i ][ j ] |= f [ i ][ j - 1 ]
return f [ m ][ n ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 class Solution {
public boolean isInterleave ( String s1 , String s2 , String s3 ) {
int m = s1 . length (), n = s2 . length ();
if ( m + n != s3 . length ()) {
return false ;
}
boolean [][] f = new boolean [ m + 1 ][ n + 1 ] ;
f [ 0 ][ 0 ] = true ;
for ( int i = 0 ; i <= m ; ++ i ) {
for ( int j = 0 ; j <= n ; ++ j ) {
int k = i + j - 1 ;
if ( i > 0 && s1 . charAt ( i - 1 ) == s3 . charAt ( k )) {
f [ i ][ j ] = f [ i - 1 ][ j ] ;
}
if ( j > 0 && s2 . charAt ( j - 1 ) == s3 . charAt ( k )) {
f [ i ][ j ] |= f [ i ][ j - 1 ] ;
}
}
}
return f [ m ][ n ] ;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 class Solution {
public :
bool isInterleave ( string s1 , string s2 , string s3 ) {
int m = s1 . size (), n = s2 . size ();
if ( m + n != s3 . size ()) {
return false ;
}
bool f [ m + 1 ][ n + 1 ];
memset ( f , false , sizeof ( f ));
f [ 0 ][ 0 ] = true ;
for ( int i = 0 ; i <= m ; ++ i ) {
for ( int j = 0 ; j <= n ; ++ j ) {
int k = i + j - 1 ;
if ( i > 0 && s1 [ i - 1 ] == s3 [ k ]) {
f [ i ][ j ] = f [ i - 1 ][ j ];
}
if ( j > 0 && s2 [ j - 1 ] == s3 [ k ]) {
f [ i ][ j ] |= f [ i ][ j - 1 ];
}
}
}
return f [ m ][ n ];
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 func isInterleave ( s1 string , s2 string , s3 string ) bool {
m , n := len ( s1 ), len ( s2 )
if m + n != len ( s3 ) {
return false
}
f := make ([][] bool , m + 1 )
for i := range f {
f [ i ] = make ([] bool , n + 1 )
}
f [ 0 ][ 0 ] = true
for i := 0 ; i <= m ; i ++ {
for j := 0 ; j <= n ; j ++ {
k := i + j - 1
if i > 0 && s1 [ i - 1 ] == s3 [ k ] {
f [ i ][ j ] = f [ i - 1 ][ j ]
}
if j > 0 && s2 [ j - 1 ] == s3 [ k ] {
f [ i ][ j ] = ( f [ i ][ j ] || f [ i ][ j - 1 ])
}
}
}
return f [ m ][ n ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 function isInterleave ( s1 : string , s2 : string , s3 : string ) : boolean {
const m = s1 . length ;
const n = s2 . length ;
if ( m + n !== s3 . length ) {
return false ;
}
const f : boolean [][] = new Array ( m + 1 ). fill ( 0 ). map (() => new Array ( n + 1 ). fill ( false ));
f [ 0 ][ 0 ] = true ;
for ( let i = 0 ; i <= m ; ++ i ) {
for ( let j = 0 ; j <= n ; ++ j ) {
const k = i + j - 1 ;
if ( i > 0 && s1 [ i - 1 ] === s3 [ k ]) {
f [ i ][ j ] = f [ i - 1 ][ j ];
}
if ( j > 0 && s2 [ j - 1 ] === s3 [ k ]) {
f [ i ][ j ] = f [ i ][ j ] || f [ i ][ j - 1 ];
}
}
}
return f [ m ][ n ];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 public class Solution {
public bool IsInterleave ( string s1 , string s2 , string s3 ) {
int m = s1 . Length , n = s2 . Length ;
if ( m + n != s3 . Length ) {
return false ;
}
bool [,] f = new bool [ m + 1 , n + 1 ];
f [ 0 , 0 ] = true ;
for ( int i = 0 ; i <= m ; ++ i ) {
for ( int j = 0 ; j <= n ; ++ j ) {
int k = i + j - 1 ;
if ( i > 0 && s1 [ i - 1 ] == s3 [ k ]) {
f [ i , j ] = f [ i - 1 , j ];
}
if ( j > 0 && s2 [ j - 1 ] == s3 [ k ]) {
f [ i , j ] |= f [ i , j - 1 ];
}
}
}
return f [ m , n ];
}
}
我们注意到,状态 $f[i][j]$ 只和状态 $f[i - 1][j]$、$f[i][j - 1]$、$f[i - 1][j - 1]$ 有关,因此我们可以使用滚动数组优化空间复杂度,将空间复杂度优化到 $O(n)$。
Python3 Java C++ Go TypeScript C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14 class Solution :
def isInterleave ( self , s1 : str , s2 : str , s3 : str ) -> bool :
m , n = len ( s1 ), len ( s2 )
if m + n != len ( s3 ):
return False
f = [ True ] + [ False ] * n
for i in range ( m + 1 ):
for j in range ( n + 1 ):
k = i + j - 1
if i :
f [ j ] &= s1 [ i - 1 ] == s3 [ k ]
if j :
f [ j ] |= f [ j - 1 ] and s2 [ j - 1 ] == s3 [ k ]
return f [ n ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 class Solution {
public boolean isInterleave ( String s1 , String s2 , String s3 ) {
int m = s1 . length (), n = s2 . length ();
if ( m + n != s3 . length ()) {
return false ;
}
boolean [] f = new boolean [ n + 1 ] ;
f [ 0 ] = true ;
for ( int i = 0 ; i <= m ; ++ i ) {
for ( int j = 0 ; j <= n ; ++ j ) {
int k = i + j - 1 ;
if ( i > 0 ) {
f [ j ] &= s1 . charAt ( i - 1 ) == s3 . charAt ( k );
}
if ( j > 0 ) {
f [ j ] |= ( f [ j - 1 ] & s2 . charAt ( j - 1 ) == s3 . charAt ( k ));
}
}
}
return f [ n ] ;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 class Solution {
public :
bool isInterleave ( string s1 , string s2 , string s3 ) {
int m = s1 . size (), n = s2 . size ();
if ( m + n != s3 . size ()) {
return false ;
}
bool f [ n + 1 ];
memset ( f , false , sizeof ( f ));
f [ 0 ] = true ;
for ( int i = 0 ; i <= m ; ++ i ) {
for ( int j = 0 ; j <= n ; ++ j ) {
int k = i + j - 1 ;
if ( i ) {
f [ j ] &= s1 [ i - 1 ] == s3 [ k ];
}
if ( j ) {
f [ j ] |= ( s2 [ j - 1 ] == s3 [ k ] && f [ j - 1 ]);
}
}
}
return f [ n ];
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 func isInterleave ( s1 string , s2 string , s3 string ) bool {
m , n := len ( s1 ), len ( s2 )
if m + n != len ( s3 ) {
return false
}
f := make ([] bool , n + 1 )
f [ 0 ] = true
for i := 0 ; i <= m ; i ++ {
for j := 0 ; j <= n ; j ++ {
k := i + j - 1
if i > 0 {
f [ j ] = ( f [ j ] && s1 [ i - 1 ] == s3 [ k ])
}
if j > 0 {
f [ j ] = ( f [ j ] || ( s2 [ j - 1 ] == s3 [ k ] && f [ j - 1 ]))
}
}
}
return f [ n ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 function isInterleave ( s1 : string , s2 : string , s3 : string ) : boolean {
const m = s1 . length ;
const n = s2 . length ;
if ( m + n !== s3 . length ) {
return false ;
}
const f : boolean [] = new Array ( n + 1 ). fill ( false );
f [ 0 ] = true ;
for ( let i = 0 ; i <= m ; ++ i ) {
for ( let j = 0 ; j <= n ; ++ j ) {
const k = i + j - 1 ;
if ( i ) {
f [ j ] = f [ j ] && s1 [ i - 1 ] === s3 [ k ];
}
if ( j ) {
f [ j ] = f [ j ] || ( f [ j - 1 ] && s2 [ j - 1 ] === s3 [ k ]);
}
}
}
return f [ n ];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 public class Solution {
public bool IsInterleave ( string s1 , string s2 , string s3 ) {
int m = s1 . Length , n = s2 . Length ;
if ( m + n != s3 . Length ) {
return false ;
}
bool [] f = new bool [ n + 1 ];
f [ 0 ] = true ;
for ( int i = 0 ; i <= m ; ++ i ) {
for ( int j = 0 ; j <= n ; ++ j ) {
int k = i + j - 1 ;
if ( i > 0 ) {
f [ j ] &= s1 [ i - 1 ] == s3 [ k ];
}
if ( j > 0 ) {
f [ j ] |= ( f [ j - 1 ] & s2 [ j - 1 ] == s3 [ k ]);
}
}
}
return f [ n ];
}
}