CLRS 15.4最长公共子序列

15.4-1
这里写图片描述
对应的LCS为 1,0,0,1,1,0
下面的程序可以验证这个答案

#include <iostream>
#include <string>
using std::cout;
using std::cin;
using std::endl;
using std::string;

void LCS_LENGTH(string &x,string &y,int **b,int **c)
{
    int m = x.size();
    int n = y.size();
    for(int i = 1; i <= m; i++)
        c[i][0] = 0;
    for(int j = 0; 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-1][j] >= c[i][j-1])
            {
                c[i][j] = c[i-1][j];
                b[i][j] = 1;    //up
            }
            else
            {
                c[i][j] = c[i][j-1];
                b[i][j] = 2;    //left
            }
        }
    }
}

void PRINT_LCS(int **b,string &x,int i,int j)
{
    if(i == 0 || j == 0)
        return;
    if(b[i][j] == 0)
    {
        PRINT_LCS(b,x,i-1,j-1);
        cout << x[i-1];
    }
    else if(b[i][j] == 1)
        PRINT_LCS(b,x,i-1,j);
    else PRINT_LCS(b,x,i,j-1);
}

int main()
{
    string x,y;
    cout << "Enter two strings:";
    cin >> x >> y;
    cout << endl;

    int m = x.size(),n = y.size();
    int **b = new int *[m+1];
    int **c = new int *[m+1];
    for(int i = 0; i <= m; i++)
    {
        b[i] = new int[n+1];
        c[i] = new int[n+1];
    }

    LCS_LENGTH(x,y,b,c);
    PRINT_LCS(b,x,m,n);
    cout << endl;
    for(int i = 0; i <= m; i++)
    {
        delete []b[i];
        delete []c[i];
    }
    delete []b;
    delete []c;
    return 0;
}

15.4-2
伪代码如下:

PRINT_LCS(c, x, y, i, j)
    if i = 0 || j = 0
        return
    if x[i] = y[j]
        PRINT_LCS(c, x, y, i-1, j-1)
        print x[i]
    elif c[i-1, j] >= c[i, j-1]
        PRINT_LCS(c, x, y, i-1, j)
    else
        PRINT_LCS(c, x, y, i, j-1)

修改第一题的LCS-LENGTH,PRINT-LCS,代码如下。

void LCS_LENGTH(string &x,string &y,int **c)
{
    int m = x.size(), n = y.size();
    for(int i = 1; i <= m; i++)
        c[i][0] = 0;
    for(int j = 0; 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;
            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];
        }
    }
}

void PRINT_LCS(int **c,string &x,string &y,int i,int j)
{
    if(i == 0 || j == 0)
        return;
    if(x[i-1] == y[j-1])
    {
        PRINT_LCS(c,x,y,i-1,j-1);
        cout << x[i-1];
    }
    else if(c[i-1][j] >= c[i][j-1])
        PRINT_LCS(c,x,y,i-1,j);
    else PRINT_LCS(c,x,y,i,j-1);
}

15.4-3
伪代码如下:

LCS-LENGTH(X, Y)
    m = X.length
    n = Y.length
    let c[0..m,0..n]and b[1..m,1..n] be new tables
    for i = 1 to m
        for j = 1 to n
            c[i,j] = -1
    LCS-LENGTH-AUX(X,Y,m,n,c,b)
    return c and b

LCS-LENGTH-AUX(X,Y,i,j,c,b)
    if c[i,j] > -1
        return c[i,j]
    if i = 0 or j = 0
        c[i,j] = 0
    else
        if X[i] == Y[j]
            c[i,j] = LCS-LENGTH-AUX(X,Y,i-1,j-1,c,b)
            b[i,j] = "↖"
        elseif LCS-LENGTH-AUX(X,Y,i,j-1,c,b) >= LCS-LENGTH-AUX(X,Y,i-1,j,c,b)
            c[i,j] = c[i,j-1]
            b[i,j] = "←"
        else  c[i,j] = c[i-1,j]
              b[i,j] = "↑"
    return c[i][j]

可运行代码如下:

int LCS_LEGNTH_AUX(string &x,string &y,int **b,int **c,int i,int j)
{
    if(c[i][j] > -1)
        return c[i][j];
    if(i == 0 || j == 0)
        c[i][j] = 0;
    else
    {
        if(x[i-1] == y[j-1])
        {
            c[i][j] = LCS_LEGNTH_AUX(x,y,b,c,i-1,j-1) + 1;
            b[i][j] = 0;
        }
        else if(LCS_LEGNTH_AUX(x,y,b,c,i-1,j) >= LCS_LEGNTH_AUX(x,y,b,c,i,j-1))
        {
            c[i][j] = LCS_LEGNTH_AUX(x,y,b,c,i-1,j);
            b[i][j] = 1;  //up
        }
        else
        {
            c[i][j] = LCS_LEGNTH_AUX(x,y,b,c,i,j-1);
            b[i][j] = 2;  //left
        }
    }
    return c[i][j];
}

void LCS_LENGTH(string &x,string &y,int **b,int **c)
{
    int m = x.size(), n = y.size();
    for(int i = 1; i <= m; i++)
        for(int j = 1; j <= n; j++)
            c[i][j] = -1;
    LCS_LEGNTH_AUX(x,y,b,c,m,n);
}

15.4-4
1、使用 2×min(m,n)表项及 O(1) 的空间
先假设m = X.length,n = Y.length,并假设 Y 的长度小于 X 的长度(否则交换一下 X,Y 即可),然后利用 c[1.2,1...n] 来计算 LCS。分别称表 c 中的第一二行为前一行和当前行;简称当前行的下一行为下一行。因为求解一个项c[i,j],只会用到 c[i1,j1],c[i,j1],c[i1,j]。所以:
将前一行全部赋值 0,从左到右计算当前行;
当前行计算完之后,将当前行的值赋值给前一行,然后当前行变成下一行(即需要计算的下一个当前行)。

2、使用 min(m,n)表项及 O(1) 的空间
原理和上面类似,不同的是,我们把 c[1...n] 分成两部分对待,计算当前行第 k 个元素时,前 k1 个已经保存在 c[1..k1] 中,而 c[k..n] 还保存的是“前一行”的内容,用一个额外的空间 val 保存 c[i1,j1] ,比较 c[k1],val,c[k] 然后更新 c[k] 即可。

15.4-5
思路就是:得到序列X 排序后的序列Y,求出X,Y的 LCS 即可。

15.4-6
O(nlgn) 求最长单调递增子序列可以在网上找到很多答案,我在这就不证明算法思想,只是简单举例说明怎么得出答案的。
假设数组 A3,2,8,9,4,我们用一个同样长度的数组 B 考察这个序列 A
首先 A[1]=3(数组从1开始计数),所以 B[1]=3,得到序列为3
接下来 A[2]=2,B[1]=3>2,更新B[1]=2,得到序列为2
接着A[3]=8>B[1]=2,将A[3] 插入到数组B,即B[2]=8,得到序列为2,8
然后A[4]=9>B[2]=8,同上使得B[3]=9;得到序列为2,8,9
最后A[5]=4,小于B[2],B[3],更新B[2]=4,此时序列为2,4,9
注意,这个2,4,9并不是我们求出的最后子序列结果,而是表示最长子序列长度为 3,那为什么要在最后一步替换2,8,9为2,4,9呢?明明正确答案就是2,8,9啊!!!因为如果数组 A 为3,2,8,9,4,7,9的话,我们按照上面所说的步骤就会得到2,4,7,9对于长度为 4 的最长单调子序列,而如果不替换则得不到这个结果!
贴一个Leetcode通过的代码

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.empty())
            return 0;
        int n = nums.size();
        int *c = new int[n];
        int LIS_len = 1;
        c[0] = nums[0];
        for(int i = 1; i < n; i++)
        {
            int index = binary_search(c,0,LIS_len - 1,nums[i]);
            if(index == LIS_len)
                c[LIS_len++] = nums[i];
            else c[index] = nums[i];
        }
        delete []c;
        return LIS_len;
    }

    int binary_search(int *array,int low,int high,int key)//返回第一个大于key的数组下标
    {
        while(low <= high)
        {
            int mid = (low + high) / 2;
            if(array[mid] == key)
                return mid;
            else if(array[mid] < key)
                low = mid + 1;
            else high = mid - 1;
        }
        if(high < 0)
            return 0;
        else return high + 1;
    }
};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值