最長(連續)公共子序列算法

問題1:
最長(不連續)公共子序列: 英文縮寫爲LCS(Longest Common Subsequence)。其定義是,一個序列S, 如果分別是兩個或多個已知序列的子序列,且是所有符合此條件序列中最長的,則S稱爲已知序列的最長公共子序列。
最長公共子序列問題是一個經典的動態規劃問題。
 
一個給定序列的子序列是從給定序列中去除一些元素,而不改變其他元素之間相對位置而得到的。
注意:此處求的是最長公共子序列,而不是最長連續公共子序列。
 
算法分析:
動態規劃的一個計算兩個序列的最長公共子序列算法如下: 
假設有:兩個序列 X、Y 爲例子:
Xm = x1 x2 x3 ... xm
Yn = y1 y2 y3 ... yn
最長公共子序列(不必連續)定義爲:
f(m, n)爲Xm和Yn之間最長的公共子序列的長度:
有f(m, 0) = f(0, m) = 0
如果xm != yn, 則f(m, n) = max{ f(m-1, n), f(m, n-1) }
如果xm = yn,則f(m, n) = f(m-1, n-1) + 1
此時,二維數組中最大的數便是 X 和 Y 的最長公共子序列的長度,依據該數組回溯,便可找出最長公共子序列。
該算法的時間和空間複雜度均爲O(m*n), 由於計算新的行的值,只需要保存上一行的值作爲依賴,而不必要前面計算的所有的行都保存,所以空間複雜度可以降爲min(O(2m),O(2n))。 
該算法的時間和空間複雜度均爲O(m*n), 但由於每次計算當前行時,只需依賴上一行的值,而不必保存所有的行,空間要min(2m+2,2n+2)個元素做中間存儲更新,空間複雜度可以降爲min(2m+2,2n+2),即O(n)。
 
C語言算法實現:
 
// 時間和空間複雜度均爲O(m*n)

#include <stdio.h>
#include <string.h>

int LCS(char szArrayA[], int iANum, char szArrayB[], int iBNum)
{
    int i, j;
    int szArray[iANum + 1][iBNum + 1];

    memset(szArray, 0, sizeof(szArray));

    for(i = 0; i < iANum; ++i)
    {
        for(j = 0; j < iBNum; ++j)
        {
            if(szArrayA[i] == szArrayB[j])
            {
                szArray[i+1][j+1] = szArray[i][j] + 1;            
            }
            else
            {    
                szArray[i+1][j+1] = szArray[i][j+1] > szArray[i+1][j] ? szArray[i][j+1] : szArray[i+1][j];
            }
            printf("%d ", szArray[i+1][j+1]);
        }
        printf("\n");
    }
    
    return szArray[iANum][iBNum];
}

// 時間複雜度爲O(m*n),空間複雜度爲O(n)。
int LCS1(char szArrayA[], int iANum, char szArrayB[], int iBNum)
{
    int i, j;
    int *pLastRow, *pRow, *pTmp;

    printf("iANum = %d, iBNum =%d\n", iANum, iBNum);

    if(iANum > iBNum)
    {
        int szArray[2*(iBNum+1)];
        memset(szArray, 0, sizeof(szArray));
        pLastRow = szArray;
        pRow = &szArray[iBNum+1];
                
        for(i = 0; i < iANum; ++i)
        {    
            for(j = 0; j < iBNum; ++j)
            {
                if(szArrayA[i] == szArrayB[j])
                {
                    pRow[j+1] = pLastRow[j] + 1;
                }
                else
                {    
                    pRow[j+1] = pLastRow[j+1] > pRow[j] ? pLastRow[j+1] : pRow[j];
                }
                printf("%d ", pRow[j+1]);
            }
            
            printf("\n");
            pTmp = pLastRow;
            pLastRow = pRow;
            pRow = pTmp;
        }
        
        return pLastRow[iBNum];
    }
    else
    {
        int szArray[2*(iANum + 1)];
        memset(szArray, 0, sizeof(szArray));
        pLastRow = szArray;
        pRow = &szArray[iANum + 1];
        
        for(i = 0; i < iBNum; ++i)
        {
            for(j = 0; j < iANum; ++j)
            {
                if(szArrayA[j] == szArrayB[i])
                {
                    pRow[j+1] = pLastRow[j] + 1;
                }
                else
                {    
                    pRow[j+1] = pLastRow[j+1] > pRow[j] ? pLastRow[j+1] : pRow[j];
                }
                printf("%d ", pRow[j+1]);
            }
            
            printf("\n");
            pTmp = pLastRow;
            pLastRow = pRow;
            pRow = pTmp;
        }
        
        return pLastRow[iANum];
    }
}

問題2:

最長連續公共子序列問題
 
算法分析:
動態規劃的一個計算兩個序列的最長連續公共子序列算法如下: 
以兩個序列 X、Y 爲例子:
設有二維數組f(i,j) 表示 X 的 i 位和 Y 的 j 位之前的最長連續公共子序列的長度,則有:
f(m,0) = f(0,m) = 0;
如果x(m) != y(n), 則f(m,n) = 0
如果x(m) = y(n), 則f(m,n) = f(m-1,n-1) + 1
該算法的時間和空間複雜度均爲O(m*n), 但由於每次計算當前行時,只需依賴上一行的值,而不必保存所有的行,空間要min(2m+2,2n+2)個元素做中間存儲更新,空間複雜度可以降爲min(2m+2,2n+2),即O(n)。
 
// 時間和空間複雜度均爲O(m*n)
int LCS2(char szArrayA[], int iANum, char szArrayB[], int iBNum)
{
    int i, j, iMaxLen = 0;
    int szArray[iANum + 1][iBNum + 1];
    
    memset(szArray, 0, sizeof(szArray));
    
    for(i = 0; i < iANum; ++i)
    {
        for(j = 0; j < iBNum; ++j)
        {
            if(szArrayA[i] == szArrayB[j])
            {
                szArray[i+1][j+1] = szArray[i][j] + 1;
                if (iMaxLen < szArray[i+1][j+1])
                {
                    iMaxLen = szArray[i+1][j+1];
                }
            }
            else
            {    
                szArray[i+1][j+1] = 0;
            }
            printf("%d ", szArray[i+1][j+1]);
        }
        printf("\n");
    }
    
    return iMaxLen;
}

// 時間複雜度爲O(m*n), 時間複雜度爲O(m*n),空間複雜度爲O(n)
int LCS3(char szArrayA[], int iANum, char szArrayB[], int iBNum)
{
    int i, j, iMaxLen = 0;
    int *pLastRow, *pRow, *pTmp;

    printf("iANum = %d, iBNum =%d\n", iANum, iBNum);

    if(iANum > iBNum)
    {
        int szArray[2*(iBNum+1)];
        memset(szArray, 0, sizeof(szArray));
        pLastRow = szArray;
        pRow = &szArray[iBNum+1];
                
        for(i = 0; i < iANum; ++i)
        {    
            for(j = 0; j < iBNum; ++j)
            {
                if(szArrayA[i] == szArrayB[j])
                {
                    pRow[j+1] = pLastRow[j] + 1;
                }
                else
                {    
                    pRow[j+1] = 0;
                }
                printf("%d ", pRow[j+1]);
            }
            
            printf("\n");
            pTmp = pLastRow;
            pLastRow = pRow;
            pRow = pTmp;
        }
        
        return pLastRow[iBNum];
    }
    else
    {
        int szArray[2*(iANum + 1)];
        memset(szArray, 0, sizeof(szArray));
        pLastRow = szArray;
        pRow = &szArray[iANum + 1];
        
        for(i = 0; i < iBNum; ++i)
        {
            for(j = 0; j < iANum; ++j)
            {
                if(szArrayA[j] == szArrayB[i])
                {
                    pRow[j+1] = pLastRow[j] + 1;
                }
                else
                {    
                    pRow[j+1] = 0;
                }
                printf("%d ", pRow[j+1]);
            }
            
            printf("\n");
            pTmp = pLastRow;
            pLastRow = pRow;
            pRow = pTmp;
        }
        
        return pLastRow[iANum];
    }
}

測試:

int main()
{
    //char szArrA[] = "1A2C3D4B56", szArrB[] = "B1D23CA45B6A";
    //char szArrA[] = "B1D23CA45B6A", szArrB[] = "1A2C3D4B56";
    
    //char szArrA[] = "1D234B56", szArrB[] = "B1D23C";
    char szArrA[] = "B1D23C", szArrB[] = "1D234B51D23C6";
    int iLen = 0;
    
    iLen = LCS(szArrA, sizeof(szArrA)/sizeof(szArrA[0])-1, szArrB, sizeof(szArrB)/sizeof(szArrB[0])-1);
    printf("lcs = %d\n", iLen);
    
    iLen = LCS1(szArrA, sizeof(szArrA)/sizeof(szArrA[0])-1, szArrB, sizeof(szArrB)/sizeof(szArrB[0])-1);
    printf("lcs1 = %d\n", iLen);

    
    iLen = LCS2(szArrA, sizeof(szArrA)/sizeof(szArrA[0])-1, szArrB, sizeof(szArrB)/sizeof(szArrB[0])-1);
    printf("lcs2 = %d\n", iLen);

    iLen = LCS3(szArrA, sizeof(szArrA)/sizeof(szArrA[0])-1, szArrB, sizeof(szArrB)/sizeof(szArrB[0])-1);
    printf("lcs3 = %d\n", iLen);
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章