两个字符串的公共子序列数

时间:2015-06-26 05:57:20

标签: algorithm language-agnostic dynamic-programming

重访最长公共子序列,我想知道2个字符串的公共子序列的数量是多少。我试图制定一个经常性的关系: DP [i] [j]表示在第一个字符串中以max i结束而在第二个字符串中以j结尾的子序列的数量。 经常性关系是:

DP[i][j]= DP[i-1][j-1] + DP[i][j-1] + DP[i-1][j] A[i]==B[j]时(A是我的第一个字符串而B是第二个字符串)

DP[i][j]=DP[i-1][j]+DP[i][j-1] other wise

没有给出正确的结果!

说明如果A [i] == B [j],那么,我们可以将A [i]添加到每个公共子序列,直到i-1和j-1形成DP [i-1] [j-1]新的后续序列。此外,我们必须添加通过从每个字符串中删除最后一个字符而形成的子序列。

另外,我们只能通过删除最后一个字符来添加方法!

如果有人可以纠正这种复发情况? Morover,我很乐意看到一个正式的证据。(只是养成了看到正式证据的习惯(你也可以提供一些提示来证明它。)

编辑:我忘了提及我正在考虑的基本情况

DP[i][0]=1所有我的A长度

和 对于所有j长度为B的DP[0][j]=1

以及

DP[0][0]=1

(表示空子序列)

2 个答案:

答案 0 :(得分:2)

DP[i-1][j]DP[i][j-1]应有DP[i-1][j-1]个公共子序列,这些子序列将被重复计算。

将您的重复更改为:

DP[i][j]= DP[i][j-1] + DP[i-1][j]

时,

A[i]==B[j]

DP[i][j]=DP[i-1][j]+DP[i][j-1]-DP[i-1][j-1]其他明智的

<强>解释
在你原来的关系中,我刚刚减去一个术语DP[i-1][j-1]。这是因为DP[i][j-1]DP[i-1][j]都包含DP[i-1][j-1]。由于我们添加了这两个术语,因此术语DP[i-1][j-1]会被重复计算,因此我们需要将其减去一次。

答案 1 :(得分:1)

#define MAX_LEN 101
string s1;
string s2;
int N1;
int N2;
int dp[MAX_LEN][MAX_LEN];
int GetCommomSubsequencesCount()
{
    for (int i = 0; i <= N1; i++)//N1 is size of 1st string
    {
        for (int j = 0; j <= N2; j++)//N2 is size of 2nd string
        {
            dp[i][j] = 0;
        }
    }
    for (int i = 1; i <= N1; i++)
    {
        for (int j = 1; j <= N2; j++)
        {
            if (s1[i - 1] == s2[j - 1])
            {
                dp[i][j] = 1 + dp[i][j - 1] + dp[i - 1][j];                    
            }
            else
            {
                dp[i][j] =  dp[i][j - 1] + dp[i - 1][j] - dp[i-1][j-1];
            }
        }
    }
    return dp[N1][N2];
}

说明:

dp [i] [j]是分别为大小为i和j的两个字符串的公共子序列数

案例1:s1 [i-1] == s2 [j-1]

所有以前的常见子序列都会加倍,因为它们会被另外一个字符追加。 dp [i] [j-1]和dp [i-1] [j]都包含dp [i-1] [j-1],因此它在我们的重现中被添加两次,这需要加倍计数所有以前的常见子序列。 复发中添加1是最新的字符匹配:由s1 [i-1]和s2 [j-1]组成的公共子序列

案例2:s1 [i-1]!= s2 [j-1]

这里我们减去dp [i-1] [j-1]一次,因为它在dp [i] [j - 1]和dp [i - 1] [j]中都存在并且被加两次。

我认为这些复发更容易被吸收,因为它们不依赖空子序列。