动态规划——最长公共子序列(LCS)

  最长公共子序列的问题描述为:

 

  下面介绍动态规划的做法。

  令 dp[i][j] 表示字符串 A 的 i 号位与字符串 B 的 j 号位之前的 LCS 长度(下标从 1 开始),如 dp[4][5] 表示 "sads" 与 “admin" 的 LCS 长度。那么可以根据 A[i] 和 B[j] 的情况,分为两种决策:

    1.  若 A[i]==B[j],则字符串 A 与字符串 B 的 LCS 增加了 1 位,即有 dp[i][j]=dp[i-1][j-1]+1。
    2.  若 A[i] != B[j],则字符串 A 的 i 号位和字符串B 的 j 号位之前的 LCS 无法延长,因此 dp[i][j] 将会继承 dp[i-1][j] 与 dp[i][j-1] 中的较大值,即有 dp[i][j]=max{dp[i-1][j], dp[i][j-1]}。    

  由此可以得到状态转移方程

        $dp[i][j]=\left\{\begin{matrix}dp[i-1][j-1]+1,A[i]==B[j]\\ max\left \{ dp[i-1][j], dp[i][j-1] \right\},A[i]!=B[j]\end{matrix}\right.$

  边界:dp[i][0] = dp[0][j] = 0 (0≤i≤n, 0≤j≤m)

  这样状态 dp[i][j] 只与其之前的状态有关,由边界出发即可得到整个 dp 数组,最终 dp[n][m] 就是需要的答案,时间复杂度为 O(nm)。

  代码如下:

 1 /*
 2     最长公共子序列(LCS) 
 3 */
 4 
 5 #include <stdio.h>
 6 #include <string.h>
 7 #include <math.h>
 8 #include <stdlib.h>
 9 #include <time.h>
10 #include <stdbool.h>
11 
12 #define maxn 100
13 char A[maxn], B[maxn];
14 int dp[maxn][maxn]; 
15 
16 // 求较大值
17 int max(int a, int b) {
18     return a>b ? a : b; 
19 } 
20 
21 int main() {
22     scanf("%s %s", A+1, B+1);        // 从下标为 1 开始输入 
23     int lenA = strlen(A+1);            // 读取长度也从 1 开始 
24     int lenB = strlen(B+1);
25     int i, j;
26     for(i=0; i<=lenA; ++i) {        // 边界 
27         dp[i][0] = 0;
28     } 
29     for(j=0; j<=lenB; ++j) {
30         dp[0][j] = 0;
31     }
32     for(i=1; i<=lenA; ++i) {        // 状态转移方程 
33         for(j=1; j<=lenB; ++j) {
34             if(A[i] == B[j]) {
35                 dp[i][j] = dp[i-1][j-1] + 1;
36             } else {
37                 dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
38             }
39         }
40     }
41     printf("%d\n", dp[lenA][lenB]);    // dp[lenA][lenB] 是答案 
42 
43     return 0;
44 }

 

posted @ 2018-02-09 11:22  Just_for_Myself  阅读(487)  评论(0编辑  收藏  举报