对于最长公共子序列的理解。

 解决LCS问题,需要把原问题分解成若干个子问题,所以需要刻画LCS的特征。

       设A=“a0,a1,…,am”,B=“b0,b1,…,bn”,且Z=“z0,z1,…,zk”为它们的最长公共子序列。不难证明有以下性质:
       如果am=bn,则zk=am=bn,且“z0,z1,…,z(k-1)”是“a0,a1,…,a(m-1)”和“b0,b1,…,b(n-1)”的一个最长公共子序列;
       如果am!=bn,则若zk!=am,蕴涵“z0,z1,…,zk”是“a0,a1,…,a(m-1)”和“b0,b1,…,bn”的一个最长公共子序列;
       如果am!=bn,则若zk!=bn,蕴涵“z0,z1,…,zk”是“a0,a1,…,am”和“b0,b1,…,b(n-1)”的一个最长公共子序列。

       

感谢这个博主,这意思是,求最长公共子序列,你肯定要比较A字串M个和B字串N个中的元素是否相同,那怎么记录呢,就想到用二维数组,但是你不能记录每一个是否相同吧,记为0和1的话太没用了,那怎么办,利用动态规划,前面比较过的,你可以拿来用啊,这就用到了递归思想,意思就是每次只比较A的B的最后一个,如果相同,则+1,并且记录寻找的路径,如果不同,就寻找减去最后一个元素的两种谁的公共子序列比较长,就选谁(比如呢,A的最后一个是不是没用了,那就比较A减去倒数第一个和B进行比较,但是还可能B在增长,那么就是B减去倒数第一个)在这里,我用二维数组c存有多少个公共子序列,b存寻找的方向,1代表斜着,2代表向上边,3代表左边

我们可以发现,假设我需要求 a1 ... am 和 b1 .. b(n-1)的LCS 和 a1 ... a(m-1) 和 b1 .. bn的LCS,一定会递归地并且重复地把如a1... a(m-1) 与 b1 ... b(n-1) 的 LCS 计算几次。所以我们需要一个数据结构来记录中间结果,避免重复计算。

        假设我们用c[i,j]表示Xi 和 Yj 的LCS的长度(直接保存最长公共子序列的中间结果不现实,需要先借助LCS的长度)。其中X = {x1 ... xm},Y ={y1...yn},Xi = {x1 ... xi},Yj={y1... yj}。可得递归公式如下:

借用  https://blog.csdn.net/hrn1216/article/details/51534607 博主的图。

借用博主的图来说明

初始化为0,你发现这样,对于(1.1)就是1和3的比较,对于(1.2)就是A(1),B(3.5)的比较,然后依次,发现都为0,B依次增长,然后A增长,(2.1)就是A(1.3) B(3)的比较,诶,出现了,就是如图记录为坐标(2-1,1-0)的值+1,放入到(2.1)中,这意味着什么呢,就是A,B串各减去一,只留各自最后一个作比较,好了,如图

然后就是B递增,(2.2)就是A(1.3)和B(3.5)发现3和5不同,那就max吧,看是A减去最后一个的子串和B比较得出的公共子序列长呢还是B减去最后一个子串和A比较的公共子序列长呢。发现是(2.1)=1比较长,也就是A(1.3)和B(3),然后顺延咯

最后得出如图

OK,下面贴出实现这个表格的代码

void LCSLength(int m, int n, char *x, char *y, int (*c)[15], int (*b)[15])
{
    int i, j;
    for (int i = 0; i <= m+1; i++) c[i][0] = 0;
    for (int i = 0; i <= n+1; i++) c[0][i] = 0;
    for (int i = 1; i<m+1; i++)
        for (int j = 1; j <= n+1; j++)
        {
            if (x[i] == y[j]) { c[i][j] = c[i - 1][j - 1] + 1; b[i][j] = 1; }
            else if (c[i - 1][j] >= c[i][j - 1]) { c[i][j] = c[i - 1][j]; b[i][j] = 2; }
            else { c[i][j] = c[i][j - 1]; b[i][j] = 3; }
        }
}

初始化,然后遍历m*n的列表,

{

如果A和B串最后一个比较相同,+1

不然{

要么c(i-1,j)>c(i,j-1),则延续上边

}

不然{

要么c(i,j-1)>c(i-1,j),则延续左边

}

}

下面贴出寻找的代码,寻找是倒着寻找的,从二维数组的最后一个,到第(0.0),只有出现1,也就是很明显的A和B的最后一个相等,才会输出嘛,不然就意味这这俩不等,是延续上面的嘛。

void LCS(int i,int j, char *x, int (*b)[15])
{
    if (i == 0 || j == 0) return;
    if (b[i][j] == 1) { LCS(i - 1,j - 1, x, b); cout << x[i] << "  "; }
    else if (b[i][j] == 2) LCS(i - 1, j, x, b);
    else LCS(i, j - 1, x, b);
}

OK,下面就是主函数了,不做说明了,C++学得不好

int main()
{
    cout << "请输入序列的长度" << endl;
    int m, n;
    cin >> m >> n;
    cout << "请输入公共子序列一" << endl;
    char x[100], y[100];
    for (int i = 1; i <= m; i++)
    {
        cin >> x[i];
        cout << x[i];
    }
    
    for (int j = 1; j <= n; j++)
        cin >> y[j];
    int c[15][15];
    int b[15][15];
    LCSLength(m, n, x, y, c,b);
    for (int i = 0; i < m+1; i++) {
        for (int j = 0; j < n+1; j++)
        {
            cout << c[i][j] << " ";
        }
        cout << endl;
    }
    LCS(m, n, x, b);
    system("pause");
}

猜你喜欢

转载自www.cnblogs.com/czrb/p/9141016.html