最長公共子序列(LCS)

裸題:
給兩串序列,求其中最長的公共子序列的長度。
如:
A:1232193
B:482914132
最長公共子序列:232/213/293…總之長度爲3。數組

固然也有一種變式,題意以下。
兩條平行線上各有n,m個點,已知每一個點的座標,一個點只能與另外一條線上的點連一條線段,兩點連成的線段不能相交。問最多連多少條線段。(線段不相交問題)markdown


如何計算?
咱們發現這個問題是一個明顯的動態規劃
經過求子序列的最長公共子序列來獲得更長的序列的最長公共子序列。spa

那麼如何構造轉移方程
用dp[i][j]表示第一序列到第i個,第二序列到第j個的最長子序列長度。debug


那麼轉移方程式以下:調試

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]要在本次用到。code

在沒有滾動數組的狀況下,空間複雜度和時間複雜度都是O(n*m),即O(N^2)。
那麼完整代碼以下:string

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)。
只須要把轉移方程式改一下:class

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

相關文章
相關標籤/搜索