动态规划
字符串
题目描述
给你两个字符串 s
和 t
,统计并返回在 s
的 子序列 中 t
出现的个数,结果需要对 109 + 7 取模。
示例 1:
输入: s = "rabbbit", t = "rabbit"
输出 : 3
解释:
如下所示, 有 3 种可以从 s 中得到 "rabbit" 的方案。
rabb bit
ra bbbit
rab bbit
示例 2:
输入: s = "babgbag", t = "bag"
输出 : 5
解释:
如下所示, 有 5 种可以从 s 中得到 "bag" 的方案。
ba bg bag
ba bgbag
b abgbag
bab gbag
babgbag
提示:
1 <= s.length, t.length <= 1000
s
和 t
由英文字母组成
解法
方法一:动态规划
我们定义 \(f[i][j]\) 表示字符串 \(s\) 的前 \(i\) 个字符中,子序列构成字符串 \(t\) 的前 \(j\) 个字符的方案数。初始时 \(f[i][0]=1\) ,其中 \(i \in [0,m]\) 。
当 \(i \gt 0\) 时,考虑 \(f[i][j]\) 的计算:
当 \(s[i-1] \ne t[j-1]\) 时,不能选取 \(s[i-1]\) ,因此 \(f[i][j]=f[i-1][j]\) ;
否则,可以选取 \(s[i-1]\) ,此时 \(f[i][j]=f[i-1][j-1]\) 。
因此我们有如下的状态转移方程:
\[
f[i][j]=\left\{
\begin{aligned}
&f[i-1][j], &s[i-1] \ne t[j-1] \\
&f[i-1][j-1]+f[i-1][j], &s[i-1]=t[j-1]
\end{aligned}
\right.
\]
最终的答案即为 \(f[m][n]\) ,其中 \(m\) 和 \(n\) 分别是字符串 \(s\) 和 \(t\) 的长度。
时间复杂度 \(O(m \times n)\) ,空间复杂度 \(O(m \times n)\) 。
我们注意到 \(f[i][j]\) 的计算只和 \(f[i-1][..]\) 有关,因此,我们可以优化掉第一维,这样空间复杂度可以降低到 \(O(n)\) 。
Python3 Java C++ Go TypeScript Rust
1
2
3
4
5
6
7
8
9
10
11
12 class Solution :
def numDistinct ( self , s : str , t : str ) -> int :
m , n = len ( s ), len ( t )
f = [[ 0 ] * ( n + 1 ) for _ in range ( m + 1 )]
for i in range ( m + 1 ):
f [ i ][ 0 ] = 1
for i , a in enumerate ( s , 1 ):
for j , b in enumerate ( t , 1 ):
f [ i ][ j ] = f [ i - 1 ][ j ]
if a == b :
f [ i ][ j ] += f [ i - 1 ][ j - 1 ]
return f [ m ][ n ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 class Solution {
public int numDistinct ( String s , String t ) {
int m = s . length (), n = t . length ();
int [][] f = new int [ m + 1 ][ n + 1 ] ;
for ( int i = 0 ; i < m + 1 ; ++ i ) {
f [ i ][ 0 ] = 1 ;
}
for ( int i = 1 ; i < m + 1 ; ++ i ) {
for ( int j = 1 ; j < n + 1 ; ++ j ) {
f [ i ][ j ] = f [ i - 1 ][ j ] ;
if ( s . charAt ( i - 1 ) == t . charAt ( j - 1 )) {
f [ i ][ j ] += f [ i - 1 ][ 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 class Solution {
public :
int numDistinct ( string s , string t ) {
int m = s . size (), n = t . size ();
unsigned long long f [ m + 1 ][ n + 1 ];
memset ( f , 0 , sizeof ( f ));
for ( int i = 0 ; i < m + 1 ; ++ i ) {
f [ i ][ 0 ] = 1 ;
}
for ( int i = 1 ; i < m + 1 ; ++ i ) {
for ( int j = 1 ; j < n + 1 ; ++ j ) {
f [ i ][ j ] = f [ i - 1 ][ j ];
if ( s [ i - 1 ] == t [ j - 1 ]) {
f [ i ][ j ] += f [ i - 1 ][ 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 func numDistinct ( s string , t string ) int {
m , n := len ( s ), len ( t )
f := make ([][] int , m + 1 )
for i := range f {
f [ i ] = make ([] int , n + 1 )
}
for i := 0 ; i <= m ; i ++ {
f [ i ][ 0 ] = 1
}
for i := 1 ; i <= m ; i ++ {
for j := 1 ; j <= n ; j ++ {
f [ i ][ j ] = f [ i - 1 ][ j ]
if s [ i - 1 ] == t [ j - 1 ] {
f [ i ][ j ] += f [ i - 1 ][ j - 1 ]
}
}
}
return f [ m ][ n ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 function numDistinct ( s : string , t : string ) : number {
const m = s . length ;
const n = t . length ;
const f : number [][] = new Array ( m + 1 ). fill ( 0 ). map (() => new Array ( n + 1 ). fill ( 0 ));
for ( let i = 0 ; i <= m ; ++ i ) {
f [ i ][ 0 ] = 1 ;
}
for ( let i = 1 ; i <= m ; ++ i ) {
for ( let j = 1 ; j <= n ; ++ j ) {
f [ i ][ j ] = f [ i - 1 ][ j ];
if ( s [ i - 1 ] === t [ j - 1 ]) {
f [ i ][ j ] += f [ i - 1 ][ 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
25
26 impl Solution {
#[allow(dead_code)]
pub fn num_distinct ( s : String , t : String ) -> i32 {
let n = s . len ();
let m = t . len ();
let mut dp : Vec < Vec < u64 >> = vec! [ vec! [ 0 ; m + 1 ]; n + 1 ];
// Initialize the dp vector
for i in 0 ..= n {
dp [ i ][ 0 ] = 1 ;
}
// Begin the actual dp process
for i in 1 ..= n {
for j in 1 ..= m {
dp [ i ][ j ] = if s . as_bytes ()[ i - 1 ] == t . as_bytes ()[ j - 1 ] {
dp [ i - 1 ][ j ] + dp [ i - 1 ][ j - 1 ]
} else {
dp [ i - 1 ][ j ]
};
}
}
dp [ n ][ m ] as i32
}
}
方法二
Python3 Java C++ Go TypeScript
class Solution :
def numDistinct ( self , s : str , t : str ) -> int :
n = len ( t )
f = [ 1 ] + [ 0 ] * n
for a in s :
for j in range ( n , 0 , - 1 ):
if a == t [ j - 1 ]:
f [ j ] += f [ j - 1 ]
return f [ n ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 class Solution {
public int numDistinct ( String s , String t ) {
int n = t . length ();
int [] f = new int [ n + 1 ] ;
f [ 0 ] = 1 ;
for ( char a : s . toCharArray ()) {
for ( int j = n ; j > 0 ; -- j ) {
char b = t . charAt ( j - 1 );
if ( a == b ) {
f [ j ] += f [ j - 1 ] ;
}
}
}
return f [ n ] ;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 class Solution {
public :
int numDistinct ( string s , string t ) {
int n = t . size ();
unsigned long long f [ n + 1 ];
memset ( f , 0 , sizeof ( f ));
f [ 0 ] = 1 ;
for ( char & a : s ) {
for ( int j = n ; j ; -- j ) {
char b = t [ j - 1 ];
if ( a == b ) {
f [ j ] += f [ j - 1 ];
}
}
}
return f [ n ];
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13 func numDistinct ( s string , t string ) int {
n := len ( t )
f := make ([] int , n + 1 )
f [ 0 ] = 1
for _ , a := range s {
for j := n ; j > 0 ; j -- {
if b := t [ j - 1 ]; byte ( a ) == b {
f [ j ] += f [ j - 1 ]
}
}
}
return f [ n ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14 function numDistinct ( s : string , t : string ) : number {
const n = t . length ;
const f : number [] = new Array ( n + 1 ). fill ( 0 );
f [ 0 ] = 1 ;
for ( const a of s ) {
for ( let j = n ; j ; -- j ) {
const b = t [ j - 1 ];
if ( a === b ) {
f [ j ] += f [ j - 1 ];
}
}
}
return f [ n ];
}