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

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

本题的最优子结构比较难找,书上直接给出了定理,定理给出三种情况

  1. 两个串最后一个字母相等,那么最长序列等于两个串都除去最后一个字母的最长序列+1
  2. 最后一个字母不相等,最长序列是两个串各除去最后一个字母后的最长序列的较大者。

这样原问题的最优解就包含了子问题的最优解(除去最后一个字母的串的求解),符合最优子结构。

根据子结构写出递归式,用二维数组记录解空间,然后根据求c[i][j]时需要依赖的表项构造顺序求解。(依赖于左,左上,上的表项)

LCS(x,y) =

(1) LCS(x - 1,y - 1) + 1 如果Ax = By

(2) max(LCS(x – 1, y) , LCS(x, y – 1)) 如果Ax ≠ By

(3) 0 如果x = 0或者y = 0

解空间:
这里写图片描述

int c[100][100];
int lcs_length(const char *strA, const char *strB)
{
    int lenA = strlen(strA);
    int lenB = strlen(strB);
    for (int i = 0; i <= lenA; i++) {
        c[0][i] = 0;
    }
    for (int i = 0; i <= lenB; i++) {
        c[i][0] = 0;
    }
    //求c[i][j]根据递归式,依赖于表格左,左上,上,所以求解顺序为行主序
    for (int i = 1; i <= lenA; i++) {
        for (int j = 1; j <= lenB; j++) {
          //上面下标都和表格对齐,字符串由于从0下标开始
            if (strA[i-1] == strB[j-1]) {
                c[i][j] = c[i - 1][j - 1] + 1;
            }
            else if (c[i][j - 1] >= c[i - 1][j]) {
                c[i][j] = c[i][j - 1];
            }
            else {
                c[i][j] = c[i-1][j];
            }
        }
    }
    return c[lenA][lenB];
}

构造最优解

在如上图的解空间里,每一行都是当前规模问题的最优解,用箭头记录了当前规模最优解的来路。

所以再顺着箭头递归找回去就可以了。

int c[100][100];
int s[100][100];
int lcs_length(const char *strA, const char *strB)
{
    int lenA = strlen(strA);
    int lenB = strlen(strB);
    for (int i = 0; i <= lenA; i++) {
        c[0][i] = 0;
    }
    for (int i = 0; i <= lenB; i++) {
        c[i][0] = 0;
    }
    //求c[i][j]根据递归式,依赖于表格左,左上,上,所以求解顺序为行主序
    for (int i = 1; i <= lenA; i++) {
        for (int j = 1; j <= lenB; j++) {
            if (strA[i-1] == strB[j-1]) {
                c[i][j] = c[i - 1][j - 1] + 1;
                s[i][j] = 'Q';  //Q代表斜,L左,U上
            }
            else if (c[i][j - 1] > c[i - 1][j]) {
                c[i][j] = c[i][j - 1];
                s[i][j] = 'L';
            }
            else {
                c[i][j] = c[i-1][j];
                s[i][j] = 'U';
            }
        }
    }
    for (int i = 1; i <= lenA; i++) {
        for (int j = 1; j <= lenB; j++) {
            printf("%c ", s[i][j]);
        }
        printf("\n");
    }
    return c[lenA][lenB];
}
void print_lcs(int lenA, int lenB, const char *strA)
{
    if (0 == lenA || 0 == lenB) {
        return;
    }

    if (s[lenA][lenB] == 'Q') {
        print_lcs(lenA - 1, lenB - 1, strA);
        printf("%c ", strA[lenA-1]);
    }
    else if (s[lenA][lenB] == 'L') {
        print_lcs(lenA, lenB - 1, strA);
    }
    else {
        print_lcs(lenA-1, lenB, strA);
    }
}

猜你喜欢

转载自blog.csdn.net/hanzheng6602/article/details/80176187