题目
动态规划
思路
此题是动态规划经典题。在《算法导论》一书中作为例题,有详细的推导和讲解过程,这里并不班门弄斧地讲解思路。直接给出算法过程:
- 设立一个二维 d p dp dp 数组,其中 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示字符串 a a a 的前 i i i 个字符和字符串 b b b 的前 j j j 个字符的最长公共子序列的长度。
- 遍历两个字符串,如果 a [ i ] = b [ j ] a[i]=b[j] a[i]=b[j] 则说明这两个字符是最长公共子序列中的一部分,此位置 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 dp[i][j]=dp[i-1][j-1]+1 dp[i][j]=dp[i−1][j−1]+1,说明应该是两个字符串的该字符前面的字符串的最长公共子序列长度再加 1 1 1;如果 a [ i ] ≠ b [ j ] a[i]\not=b[j] a[i]=b[j] 则说明这两个字符不是最长公共子序列中的一部分,此位置 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) dp[i][j]=max(dp[i-1][j],dp[i][j-1]) dp[i][j]=max(dp[i−1][j],dp[i][j−1]),说明应该是两个字符串的该字符前面的字符串的最长公共子序列长度的最大值;
具体实现见代码,需要注意的是本题有多组输入。如果觉得不想每次输入都重新分配 d p dp dp 数组的空间的话,可以将下面的代码进行优化。
代码
#include <stdio.h>
#include <string.h>
#define N 5005
// 求字符串 a 和 b 的最长公共子序列的长度
int LCS(char* a, char* b) {
int n = strlen(a), m = strlen(b);
int i = 0, j = 0;
int dp[n + 1][m + 1];
for (i = 0; i <= n; i++) {
dp[i][0] = 0;
}
for (i = 0; i <= m; i++) {
dp[0][i] = 0;
}
for (i = 1; i <= n; i++) {
for (j = 1; j <= m; j++) {
if (a[i - 1] != b[j - 1]) {
dp[i][j] = dp[i - 1][j] < dp[i][j - 1] ? dp[i][j - 1] : dp[i - 1][j];
} else {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
}
}
return dp[n][m];
}
int main(void) {
char a[N], b[N];
// 多组输入
while (~scanf("%s%s", a, b)) {
printf("%d\n", LCS(a, b));
}
return 0;
}