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

Posted dalgleish

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划之最长公共子序列相关的知识,希望对你有一定的参考价值。

原理请参考《算法导论》

定义常量

enum {upper_left, up, left};
#define LENGTHA (sizeof(A)/sizeof(A[0]))
#define LENGTHB (sizeof(B)/sizeof(B[0]))

版本1,带辅助数组b

int lcsLength(int *A, int *B, int m, int n, int ***c, int ***b) {
    int i, j;
    if (m <= 0 || n <= 0) return -1;
    *c = new int *[m + 1];
    *b = new int *[m + 1];
    for (int i = 0; i <= m; i++) {
        (*c)[i] = new int[n + 1];
        (*b)[i] = new int[n + 1];
    }
    //初始化边界
    for (i = 1; i <= m; i++)
        (*b)[i][0] = (*c)[i][0] = 0;
    for (j = 0; j <= n; j++)
        (*b)[0][j] = (*c)[0][j] = 0;
    for (i = 1; i <= m; i++)
        for (j = 1; j <= n; j++) {
            if (A[i - 1] == B[j - 1]) {
                (*c)[i][j] = (*c)[i - 1][j - 1] + 1;
                (*b)[i][j] = upper_left;
            }
            else {
                if ((*c)[i - 1][j] >= (*c)[i][j - 1]) {
                    (*c)[i][j] = (*c)[i - 1][j];
                    (*b)[i][j] = up;
                }
                else {
                    (*c)[i][j] = (*c)[i][j - 1];
                    (*b)[i][j] = left;
                }
            }
        }
    return (*c)[m][n];
}

对应输出函数

void lcsprintf(int **b, int *A, int i, int j) {
    if (i == 0 || j == 0) return;
    if (b[i][j] == upper_left) {
        lcsprintf(b, A, i - 1, j - 1);
        printf("%C ", A[i-1]);
    }
    else if (b[i][j] == up)
        lcsprintf(b, A, i - 1, j);
    else
        lcsprintf(b, A, i, j - 1);
}

版本2,不带辅助数组b

int lcsLength1(int *A, int *B, int m, int n, int ***c) {
    int i, j;
    if (m <= 0 || n <= 0) return -1;
    *c = new int *[m + 1];
    for (int i = 0; i <= m; i++) 
        (*c)[i] = new int[n + 1];
    //初始化边界
    for (i = 1; i <= m; i++)
        (*c)[i][0] = 0;
    for (j = 0; j <= n; j++)
        (*c)[0][j] = 0;
    for (i = 1; i <= m; i++)
        for (j = 1; j <= n; j++) {
            if (A[i - 1] == B[j - 1]) 
                (*c)[i][j] = (*c)[i - 1][j - 1] + 1;
            else {
                if ((*c)[i - 1][j] >= (*c)[i][j - 1]) 
                    (*c)[i][j] = (*c)[i - 1][j];
                else 
                    (*c)[i][j] = (*c)[i][j - 1];
            }
        }
    return (*c)[m][n];
}

对应输出函数

void lcsprintf1(int **c, int *A,int *B, int i, int j) {
    if (i==0 || j == 0) return;
    if (A[i - 1] == B[j - 1]) {
            lcsprintf1(c, A, B, i - 1, j - 1);
            printf("%C ", A[i-1]);
    }
    else {
        if (c[i - 1][j] >= c[i][j - 1])
            lcsprintf1(c, A, B, i - 1, j);
        else
            lcsprintf1(c, A, B, i, j - 1);
    }
}

最后,打印所有可能函数

void lcsprintfAll(int **c, int *A, int *B, int i, int j, std::string str) {
    while (i > 0 && j > 0) {
        if (A[i - 1] == B[j - 1]) {
            str = (char)A[i - 1] + (" " + str);
            i--; 
            j--;
        }
        else {
            if (c[i - 1][j] > c[i][j - 1]) //向左走  
                i--;
            else if (c[i - 1][j] < c[i][j - 1]) //向上走  
                j--;
            else {
                lcsprintfAll(c, A, B, i - 1, j, str);
                lcsprintfAll(c, A, B, i, j - 1, str);
                return;
            }
        }
    }
    printf("%s\\n", str.c_str());
}

Main函数

int main()
{
    int A[] = { \'A\',\'B\',\'C\',\'B\',\'D\',\'A\',\'B\' }, B[] = {\'B\',\'D\',\'C\',\'A\',\'B\',\'A\' };
    int **c, **b;
    printf("Max %d\\n",lcsLength(A, B, LENGTHA, LENGTHB, &c, &b));
    lcsprintf(c, LENGTHA + 1, LENGTHB + 1);//多申请了一个边界
    lcsprintf(b, LENGTHA + 1, LENGTHB + 1);
    lcsprintf(b, A, LENGTHA, LENGTHB);
    lcsfree(c, LENGTHA + 1);
    lcsfree(b, LENGTHA + 1);
    printf("\\n优化后的代码...\\n");
    printf("Max %d\\n", lcsLength1(A, B, LENGTHA, LENGTHB, &c));
    lcsprintf(c, LENGTHA + 1, LENGTHB + 1);
    lcsprintf1(c, A, B, LENGTHA, LENGTHB);
    printf("\\n打印所有可能\\n");
    std::string str;
    lcsprintfAll(c, A, B, LENGTHA, LENGTHB, str);
    lcsfree(c, LENGTHA + 1);
    return 0;
}
//A: \'A\',\'B\',\'C\',\'B\',\'D\',\'A\',\'B\'
//B: \'B\',\'D\',\'C\',\'A\',\'B\',\'A\'

辅助函数

void lcsprintf(int **b, int m, int n) {
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++)
            printf("%04d  ", b[i][j]);
        printf("\\n");
    }
    printf("\\n");
}

void lcsfree(int **p, int n) {
    for (int i = 0; i < n; i++)
        delete[] p[i];
    delete[] p;
}

 

打印结果:

所有代码均经过测试,结果正确。

 

以上是关于动态规划之最长公共子序列的主要内容,如果未能解决你的问题,请参考以下文章

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

最长公共子序列(LCS)动态规划解题笔记

最长公共子序列(LCS)动态规划解题笔记

动态规划——最长公共子序列与最长公共子串 (含Python实现代码)

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

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