最长公共子串

问题描述:

     求解两个字符串的最长公共子串

参考资料:

1、http://blog.csdn.net/yysdsyl/article/details/4226630

2、http://www.cnblogs.com/huangxincheng/archive/2012/11/11/2764625.html

问题解决:

    使用动态规划,考虑如何将原问题转换为子问题,假设字符串X形如x1 x2 x3 x4....xm

字符串Y形如y1 y2 y3...yn,考虑X和Y的最长公共子串:

   若x1=y1,则原问题可以转化为求解x2 x3 x4...xm与y2 y3 y4...yn的最长公共子串

    若x1!=y1,则原问题可以转换为求解x2 x3 x4...xm 与 y1 y2 y3 y4...yn 或者 x1 x2 x3 x4...xm  与y2 y3 y4...yn 的最长公共子串

    现在使用二维数组c[i][j]表示字符串X[i]的前i个元素与Y[j]的前j个元素的最长公共子串长度,则有:

clipboard

以字符串X="ABCBDAB"  和字符串Y="BDCABA" 为例子,生成的c[i][j]如下所示:

clipboard[1]

        回溯输出字符串,使用一个二维数组b[i][j]记录c[i][j]通过哪一个子问题求得,如下:

clipboard[2]

也就是,当b[i][j]=0相当于如上图中的  左上箭头,b[i][j]=1 为如上图的向上箭头

b[i][j]=-1为如上图的向左箭头。

代码文件:

/**
*    求解最长公共子序列
*    满足动态规划的条件,使用动态规划的思想求解。
*        考虑最长公共子序列问题如何分解成子问题,设X=“x0,x1,…,xm-1”,Y=“y0,y1,…,ym-1”,
*    并Z=“z0,z1,…,zk-1”为它们的最长公共子序列。不难证明有以下性质:
*
*(1) 如果xm-1=yn-1,则zk-1=xm-1=yn-1,且“z0,z1,…,zk-2”是“x0,x1,…,xm-2”和“y0,y1,…,yn-2”的一个最长公共子序列;
*
*(2) 如果xm-1!=yn-1,则若zk-1!=xm-1,蕴涵“z0,z1,…,zk-1”是“x0,x1,…,xm-2”和“y0,y1,…,yn-1”的一个最长公共子序列;
*
*(3) 如果xm-1!=yn-1,则若zk-1!=yn-1,蕴涵“z0,z1,…,zk-1”是“x0,x1,…,xm-1”和“y0,y1,…,yn-2”的一个最长公共子序列。
*    
*
*
*   这样,在找X和Y的公共子序列时,
*    如有xm-1=yn-1,则进一步解决一个子问题,找“x0,x1,…,xm-2”和“y0,y1,…,ym-2”的一个最长公共子序列;
*    如果xm-1!=yn-1,则要解决两个子问题,找出“x0,x1,…,xm-2”和“y0,y1,…,yn-1”的一个最长公共子序列和
* 找出“x0,x1,…,xm-1”和“y0,y1,…,yn-2”的一个最长公共子序列,再取两者中较长者作为X和Y的最长公共子序列。
*
*     引进一个二维数组c[][],用c[i][j]记录X前i个子串与Y前j个子串的LCS 的长度,b[i][j]记录c[i][j]是通过哪一个子问题的值求得的,
*  以决定搜索的方向。
*     我们是自底向上进行递推计算,那么在计算c[i,j]之前,c[i-1][j-1],c[i-1][j]与c[i][j-1]均已计算出来。
*  此时我们根据X[i] = Y[j]还是X[i] != Y[j],就可以计算出c[i][j]。
*
*/
#include <iostream>
using namespace std;

void LCSLength(char* x,char* y,int m,int n,int** c,int** y)
{
    for (int i=0;i<=m;i++)
    {
        c[i][0]=0;
    }

    for (int j=1;j<=n;j++)
    {
        c[0][j]=0;
    }

    for (int i=1;i<=m;i++)
    {
        for (int j=1;j<=n;j++)
        {
            if (x[i-1]==y[j-1])    //满足转换为子问题的条件
            {
                c[i][j]=c[i-1][j-1]+1;
                b[i][j]=0;
            }
            else if (c[i][j-1]>c[i-1][j])
            {
                c[i][j]=c[i][j-1];
                b[i][j]=-1;
            }
            else
            {
                c[i][j]=c[i-1][j];
                b[i][j]=1;
            }
        }
    }

    //输出X[i]和Y[j]的最长公共子串的长度c[i][j]
    for (int i=0;i<=m;i++)
    {
        for (int j=0;j<=n;j++)
        {
            cout<<c[i][j]<<"\t";
        }
        cout<<endl;
    }
    cout<<endl;
}


//回溯,输出最长公共子串
void PrintLCS(char* x,int** c,int** b,int m,int n)
{
    if (m==0||n==0)
    {
        return ;
    }

    if (b[m][n]==0)
    {
        PrintLCS(x,c,b,m-1,n-1);
        cout<<x[m-1];
    }
    else if (b[m][n]==1)
    {
        PrintLCS(x,c,b,m-1,n);
    }
    else
    {
        PrintLCS(x,c,b,m,n-1);
    }
}

int main()
{
    char* x="ABCBDAB";
    char* y="BDCABA";

    int m=7;
    int n=6;

    int **c=new int*[m+1];
    for (int i=0;i<=m;i++)
    {
        c[i]=new int[n+1];
    }

    //new 二维数组
    int **b=new int*[m+1];
    for (int i=0;i<=m;i++)
    {
        b[i]=new int[n+1];
    }

    LCSLength(x,y,m,n,c,b);

    PrintLCS(x,c,b,m,n);
    cout<<endl;

    for (int i=0;i<=m;i++)
    {
        delete []c[i];
    }

    for (int i=0;i<=m;i++)
    {
        delete []b[i];
    }
}
posted @ 2014-02-17 20:33  罗松超  阅读(656)  评论(0编辑  收藏  举报