1. 程式人生 > >動態規劃之最長公共子序列

動態規劃之最長公共子序列

圖片 輔助 length ret %s csp TP 子序列 輸出

原理請參考《算法導論》

定義常量

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;
}

打印結果:

技術分享圖片

所有代碼均經過測試,結果正確。

動態規劃之最長公共子序列