1. 程式人生 > >動態規劃-最長公共子序列LCS

動態規劃-最長公共子序列LCS

return str2 pat for 思路 規劃 得來 表示 ||

0 問題

給定兩個字符串,求最長公共子序列LCS。

也就是說兩個字符串中都有的部分,或者理解為,兩個字符串同時都刪除字符串中的某些字符,使得最終的兩個字符串,相等,且是最長的。

1 分析

假設兩個str1,str2字符串,已經知道了最長公共子序列長度為L

那麽,當在str1和str2,兩個的尾部,同時添加一個相同的字符,比如a,那麽新的str1,和str2的最長公共子序列長度就是L+1

當str1後面添加一個字符,str2不添加,那麽最長公共子序列長度為L

反之,str1不添加,str2添加,那麽也是L

當同時添加一個字符,但是添加不同的字符,那麽長度仍為L

因此,可以考慮

int lcs(string str1, string str2)
{
    int len1 = str1.size();
    int len2 = str2.size();

    // memo[i][j] 中記錄的是,字符串從第0個字符開始,長度為i,或是j的子數組的lcs長度
    vector<vector<int>> memo(len1 + 1, vector<int>(len2 + 1, 0));

    // i,j都是長度,表示從第0個字符開始長為i或j的子字符串
    // 因此使用的是 str1[i - 1] == str2[j - 1] 來比較
    // i,j都不斷的遞增,則可認為是不斷的添加字符
    for (int i = 1; i <= str1.size(); ++i)
    {
        for (int j = 1; j <= str2.size(); ++j)
        {
            if (str1[i - 1] == str2[j - 1])
            {
                // 當兩個字符相同的時候,就認為,是比之前的子數組的lcs長度大1
                memo[i][j] = memo[i - 1][j - 1] + 1;
            }
            else
            {
                // 如果新添加的兩個字符不相同,那麽取之前的 L 
                // 但是可能是str1 加字符得來的,也可能是str2 加字符得來的,因此需要取兩個前狀態的lcs的長度最大值
                if (memo[i - 1][j] > memo[i][j - 1])
                {
                    memo[i][j] = memo[i - 1][j];
                }
                else
                {
                    memo[i][j] = memo[i][j - 1];
                }
            }
        }
    }
    return memo[len1][len2];
}

  

或者說,也可以這樣考慮,

str1,和str2,當最後一個字符相同,那麽,str1,和str2的最長公共子序列的長度應該是,str-1和str2-1的最長公共子序列長度+1

當最後一個字符不相同的時候,那麽,str1,和str2的最長公共子序列的長度應該是(str1-1和str2)與(str1和str2-1)的最長公共子序列長度的較大的那個。

但是這樣計算大量相同的問題,因此加上memo

int aux_lcs_d(string str1, int l1, string str2, int l2,vector<vector<int>> &memo)
{
    if (l1 < 0 || l2 < 0)
    {
        return 0;
    }
    if(memo[l1][l2] != 0)
    {
        return memo[l1][l2];
    }
    if (str1[l1] == str2[l2])
    {
        memo[l1][l2]= aux_lcs_d(str1, l1 - 1, str2, l2 - 1,memo) + 1;
        return memo[l1][l2];
    }
    else
    {
        memo[l1][l2]= max(aux_lcs_d(str1, l1, str2, l2 - 1,memo), aux_lcs_d(str1, l1 - 1, str2, l2,memo));
        return memo[l1][l2];
    }
}

int lcs_d(string str1,string str2)
{
    vector <vector<int>> memo(str1.size(),vector<int>(str2.size(),0));
    return aux_lcs_d(str1, str1.size()-1, str2, str2.size()-1,memo) ;
}

  

以上就是計算lcs長度的思路.

當需要計算出完成的路徑時。添加一個額外的容器,記錄,每個狀態是怎麽從前一個狀態轉移過來的

int aux_lcs_d(string str1, int l1, string str2, int l2,vector<vector<int>> &memo,vector<vector<int>> &path)
{
    if (l1 < 0 || l2 < 0)
    {
        return 0;
    }
    if(memo[l1][l2] != 0)
    {
        
        return memo[l1][l2];
    }
    if (str1[l1] == str2[l2])
    {
        path[l1][l2]=‘0‘; // 這個表示從str1 ,str2同時增長來的
        memo[l1][l2]= aux_lcs_d(str1, l1 - 1, str2, l2 - 1,memo,path) + 1;
        return memo[l1][l2];
    }
    else
    {
        int tmp1=aux_lcs_d(str1, l1, str2, l2 - 1,memo,path);
        int tmp2=aux_lcs_d(str1, l1 - 1, str2, l2,memo,path);
        memo[l1][l2]= max(tmp1,tmp2);
        
        if(tmp1==memo[l1][l2])
        {
            // 表示從 str2 增長來的
            path[l1][l2]=‘2‘;
        }else{
            // 從str1 增長來的
            path[l1][l2]=‘1‘;
        }
        return memo[l1][l2];
    }
}

  

  

動態規劃-最長公共子序列LCS