最长公共子序列
最长公共子序列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const int N = 1010; // 定义常量N,表示数组的最大大小
int n, m; // n和m分别表示两个字符串的长度
char a[N], b[N]; // 定义两个字符数组来存储输入的字符串
int f[N][N]; // 定义二维数组f用于动态规划
int main()
{
scanf("%d%d", &n, &m); // 输入两个字符串的长度
scanf("%s%s", a + 1, b + 1); // 输入两个字符串,注意从下标1开始存储
// 动态规划求解最长公共子序列
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
{
// 如果当前两个字符不相同,则公共子序列长度不增加
f[i][j] = max(f[i - 1][j], f[i][j - 1]);
// 如果当前两个字符相同,则可以在f[i-1][j-1]的基础上加1
if (a[i] == b[j]) f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
}
printf("%d\n", f[n][m]); // 输出最长公共子序列的长度
return 0; // 程序正常结束
}
注意
- 注意从下标1开始存储
- 双重循环
f[i][j] 存储的是子串 a[1…i] 和子串 b[1…j] 的最长公共子序列长度
疑问:为什么失配的时候不写成
f[i][j] =f[i-1][j-1]
?
考虑两个字符串:- a = “ABCDE”
- b = “ACE”
当我们到达i=4, j=2时:
- a[4]=”D” 和 b[2]=”C” 不匹配
- f[3][2] = 2 (子序列是”AC”)
- f[4][1] = 1 (子序列是”A”)
如果我们使用 f[i][j] = f[i-1][j-1],那么 f[4][2] = f[3][1] = 1
但明显这是错的,因为如果我们把b[2]考虑上,那么a[1…4]和b[1…2]这两个子序列就有”AC”这两个公共子序列,即 f[3][2] = 2。
所以正确的转移应该是 f[4][2] = max(f[3][2], f[4][1]) = max(2, 1) = 2。
举例
假设我们有两个字符串:
- a = “ABC” (n = 3)
- b = “AC” (m = 2)
初始状态:
f[0][0]=f[0][1]=f[0][2]=f[1][0]=f[2][0]=f[3][0] = 0
填表过程:
- i=1, j=1: 比较 a[1]=”A” 和 b[1]=”A”
- 它们相等,所以 f[1][1] = f[0][0] + 1 = 1
- i=1, j=2: 比较 a[1]=”A” 和 b[2]=”C”
- 它们不相等,所以 f[1][2] = max(f[0][2], f[1][1]) = 1
- i=2, j=1: 比较 a[2]=”B” 和 b[1]=”A”
- 它们不相等,所以 f[2][1] = max(f[1][1], f[2][0]) = 1
- i=2, j=2: 比较 a[2]=”B” 和 b[2]=”C”
- 它们不相等,所以 f[2][2] = max(f[1][2], f[2][1]) = 1
- i=3, j=1: 比较 a[3]=”C” 和 b[1]=”A”
- 它们不相等,所以 f[3][1] = max(f[2][1], f[3][0]) = 1
- i=3, j=2: 比较 a[3]=”C” 和 b[2]=”C”
- 它们相等,所以 f[3][2] = max(f[3][2], f[2][1] + 1) = max(1, 1+1) = 2
最终得到 f[3][2] = 2,即最长公共子序列的长度为2
This post is licensed under CC BY 4.0 by the author.