Skip to content

Commit 04842b4

Browse files
committed
acwing: add 897_longest_common_subsequence.go
1 parent 5bee6ac commit 04842b4

File tree

1 file changed

+70
-0
lines changed

1 file changed

+70
-0
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package main
2+
3+
/*
4+
@author: CodeWater
5+
@since: 2025/03/07
6+
@desc: 897. 最长公共子序列
7+
8+
按照a[i]、b[j]是否包含在子序列当中,我们可以划分出四种状态:
9+
1. f[i - 1][j - 1](a[i]、b[j]不在子序列当中): 也就是a[i]和b[j]这两个位置的字母不匹配,依赖于a的前i-1和b的前j-1个字母
10+
中出现的公共序列。
11+
2. f[i - 1][j - 1] + 1(a[i]、b[j]在子序列当中): a[i]和b[j]这两个位置的字母匹配, 这样的情况下用前一种状态+1即可。
12+
另外两种情况有点特殊。
13+
3. a[i]不在子序列当中,但是b[j]在, f[i - 1][j]
14+
4. a[i]在子序列当中,但是b[j]不在, f[i][j - 1]
15+
这两个状态的特殊点在于状态方程并不完全等价于划分状态的意思,比如第三种情况,a[i]不在b[j]在,方程
16+
为f[i - 1][j],但实际上这个方程表示的是所有在a前i-1个字母中和在b前j个字母中出现的最长公共子序列,
17+
并不能保证b的最后一个字母是b[j],只能保证b[j]这个字母在这个序列中出现过! 也就是说,方程实际表达
18+
的含义比划分的状态范围要更大一些!
19+
同理,第四种情况也是一样。但是这里求最长的公共子序列max,3、4两种状态重合没有关系,不影响结果。
20+
21+
另外,第三、四种情况包含了第一种情况,所以在实际代码的时候,可以不用第一种情况。
22+
23+
*/
24+
25+
import (
26+
"fmt"
27+
)
28+
29+
const N = 1010
30+
31+
var (
32+
n, m int
33+
// 这里目前用的byte数组,也可以采用string来,输入代码会更少一些
34+
// a, b string
35+
a, b [N]byte
36+
// f[i][j]: 所有在a前i个字母中出现,且在b前j个字母中出现的最长公共子序列
37+
f [N][N]int
38+
)
39+
40+
func max(a, b int) int {
41+
if a > b {
42+
return a
43+
}
44+
return b
45+
}
46+
47+
func main() {
48+
fmt.Scan(&n, &m)
49+
for i := 1; i <= n; i++ {
50+
fmt.Scanf("%c", &a[i])
51+
}
52+
fmt.Scanf("\n")
53+
for j := 1; j <= m; j++ {
54+
fmt.Scanf("%c", &b[j])
55+
}
56+
// 如果用string来存储,则需要用下面的代码来输入;然后下面的for循环中,a[i]和b[j]需要改成a[i-1]和b[j-1]
57+
//fmt.Scan(&a, &b)
58+
59+
for i := 1; i <= n; i++ {
60+
for j := 1; j <= m; j++ {
61+
f[i][j] = max(f[i-1][j], f[i][j-1])
62+
// 最后一种情况: 只有ij这两个位置的字母相等时f[i-1][j-1]才有效
63+
if a[i] == b[j] {
64+
f[i][j] = max(f[i][j], f[i-1][j-1]+1)
65+
}
66+
}
67+
}
68+
69+
fmt.Println(f[n][m])
70+
}

0 commit comments

Comments
 (0)