不同的子序列DP解释

来自LeetCode

给定一个字符串S和一个字符串T,计算S中不同子序列的个数。

字符串的子序列是由原始字符串形成的新字符串,通过删除字符中的一些(可以不是)而不干扰其余字符的相对位置。 (即“ACE”是“ABCDE”的子序列,而“AEC”不是)。

这里是一个例子:S =“兔子”,T =“兔子”

返回3。

我看到一个很好的DP解决方案,但是,我很难理解它,任何人都可以解释这个DP如何工作?

int numDistinct(string S, string T) {

    vector<int> f(T.size()+1);

    //set the last size to 1.
    f[T.size()]=1;

    for(int i=S.size()-1; i>=0; --i){
        for(int j=0; j<T.size(); ++j){
            f[j]+=(S[i]==T[j])*f[j+1];
            printf("%dt", f[j] );
        }
        cout<<"n";
    }
    return f[0];
}

首先,尝试自己解决这个问题,以提出一个天真的实现:

假设S.length = mT.length = n 。 我们写S{i}作为从i开始的S的子串。 例如,如果S = "abcde"S{0} = "abcde"S{4} = "e"S{5} = "" 。 我们对T使用类似的定义。

N[i][j]S{i}T{j}的不同子序列。 我们对N[0][0]感兴趣(因为它们都是完整的字符串)。

有两种简单的情况:对于任何iN[i][n] ,对于j<nN[m][j]""在一些字符串S中有多少个子序列? 正确1. ""多少个T ? 只有0。

现在,给定一些任意的ij ,我们需要找到一个递归公式。 有两种情况。

如果S[i] != T[j] ,我们知道N[i][j] = N[i+1][j] (我希望你自己可以证明这一点,我打算解释上面的神秘算法详细地说,不是这个天真的版本)。

如果S[i] = T[j] ,我们有一个选择。 我们可以'匹配'这些字符并继续与ST的下一个字符,或者我们可以忽略匹配(就像在S[i] != T[j] )。 既然我们有两种选择,我们需要在那里增加计数: N[i][j] = N[i+1][j] + N[i+1][j+1]


为了使用动态编程来查找N[0][0] ,我们需要填充N表。 我们首先需要设置表格的边界:

N[m][j] = 0, for 0 <= j < n
N[i][n] = 1, for 0 <= i <= m

因为在递归关系的依赖关系,我们可以填写表循环的其余部分i向后和j转发:

for (int i = m-1; i >= 0; i--) {
    for (int j = 0; j < n; j++) {
        if (S[i] == T[j]) {
            N[i][j] = N[i+1][j] + N[i+1][j+1];
        } else {
            N[i][j] = N[i+1][j];
        }
    }
}

我们现在可以使用算法的最重要的技巧:我们可以使用一个一维数组f ,并在外部循环中使用不变量: f = N[i+1]; 这是可能的,因为桌子被填满了。 如果我们将此应用于我的算法,则会得出:

f[j] = 0, for 0 <= j < n
f[n] = 1

for (int i = m-1; i >= 0; i--) {
    for (int j = 0; j < n; j++) {
        if (S[i] == T[j]) {
            f[j] = f[j] + f[j+1];
        } else {
            f[j] = f[j];
        }
    }
}

我们差不多就是你给的算法。 首先,我们不需要初始化f[j] = 0 。 其次,我们不需要赋值类型f[j] = f[j]

由于这是C++代码,我们可以重写代码片段

if (S[i] == T[j]) {
    f[j] += f[j+1];
}

f[j] += (S[i] == T[j]) * f[j+1];

就这样。 这产生了算法:

f[n] = 1

for (int i = m-1; i >= 0; i--) {
    for (int j = 0; j < n; j++) {
        f[j] += (S[i] == T[j]) * f[j+1];
    }
}

我认为答案很好,但有些东西可能不正确。

我认为我们应该通过ij向后迭代。 然后我们将数组N为数组f ,我们向前循环j以便不重叠最后得到的结果。

for (int i = m-1; i >= 0; i--) {
    for (int j = 0; j < n; j++) {
        if (S[i] == T[j]) {
            N[i][j] = N[i+1][j] + N[i+1][j+1];
        } else {
            N[i][j] = N[i+1][j];
        }
    }
}
链接地址: http://www.djcxy.com/p/61507.html

上一篇: Distinct Subsequences DP explanation

下一篇: Data Structure for Subsequence Queries