File tree Expand file tree Collapse file tree 1 file changed +70
-0
lines changed
algorithm/src/acwing/algorithmBasicCourse/5_dynamic_programming Expand file tree Collapse file tree 1 file changed +70
-0
lines changed Original file line number Diff line number Diff line change 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+ }
You can’t perform that action at this time.
0 commit comments