最长(连续)公共子序列算法

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

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