字符串
动态规划
题目描述
给你两个字符串 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 ];
}