最长公共子序列(LCS)

裸题:
给两串序列,求其中最长的公共子序列的长度。
如:
A:1232193
B:482914132
最长公共子序列:232/213/293…总之长度为3。

当然也有一种变式,题意如下。
两条平行线上各有n,m个点,已知每个点的坐标,一个点只能与另一条线上的点连一条线段,两点连成的线段不能相交。问最多连多少条线段。(线段不相交问题)


如何计算?
我们发现这个问题是一个明显的动态规划
通过求子序列的最长公共子序列来得到更长的序列的最长公共子序列。

那么如何构造转移方程
用dp[i][j]表示第一序列到第i个,第二序列到第j个的最长子序列长度。


那么转移方程式如下:

if(a[i] == b[j])    dp[i][j] = dp[i-1][j-1]+1;
else dp[i][j] = max(dp[i-1][j], dp[i][j-1]);

如果当前位置的两个数(字符)相同,那么它的”基础“不能取到dp[i][j-1]或dp[i-1][j]。因为a[i]和b[j]要在本次用到。

在没有滚动数组的情况下,空间复杂度和时间复杂度都是O(n*m),即O(N^2)。
那么完整代码如下:

void Dp1()
{
    for(unsigned i = 1; i != n+1; ++i)
    {
        for(unsigned j = 1; j != m+1; ++j)
        {
            if(a[i-1] == b[j-1])
            {
                dp[i][j] = dp[i-1][j-1]+1;
            }
            else
            {
                dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
            }
            debug("%d ", dp[i][j]); //调试输出
        }
        debug("\n"); //同上
    }   
    return ;
}

我们可以发现转移方程只用到了dp[i-1][j],dp[i][j-1],dp[i-1][j-1],至于dp[i-2][j]等根本没有用到,所以我们可以滚动数组只留下两行(一行存上一次,一行存当前)。
空间复杂度降到O(2*m),即O(N)。
只需要把转移方程式改一下:

void Dp()
{
    for(unsigned i = 1; i != n+1; ++i)
    {
        for(unsigned j = 1; j != m+1; ++j)
        {
            if(a[i-1] == b[j-1])
            {
                dp[i&1][j] = dp[(i&1)^1][j-1]+1;
            }
            else
            {
                dp[i&1][j] = max(dp[(i&1)^1][j], dp[i&1][j-1]);
            }
            debug("%d ", dp[i][j]);
        }
        debug("\n");
    }
    return ;
}

利用运算:

x&1 == x%2//异
(x&1)^1 == (i-1)%2

效果相同但是效率更高


自此结束。

箜瑟_qi 2017.04.25 17:57

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值