Google Jam 2009. C.欢迎使用Code Jam。无法理解动态编程

时间:2013-11-11 21:42:16

标签: algorithm dynamic-programming

问题的原始链接在这里:https://code.google.com/codejam/contest/90101/dashboard#s=p2&a=2

简单地说,我们需要找到字符串S =“welcome to code jam”的次数作为给定字符串S的子序列出现,例如, S =“欢迎代码堵塞” T =“wweellccoommee代码qps jam”

我知道理论,但在实践中并不擅长DP。您能否解释一下逐步解决这个DP问题的过程及其工作原理?

3 个答案:

答案 0 :(得分:1)

用简单的术语解释:

         if(S(i) == T(k))

           Subseq(i,k) = Subseq(i+1,k+1) + Subseq(i,k+1)

         else Subseq(i,k) = Subseq(i,k+1)

其中i表示子串S [i to end]

其中k表示子串T [k to end]

其中Subseq(i,k)= T [k to end]中S [i to end]的子序列数

其中S(i)= S中第i个索引处的字符

其中T(k)= T

中第k个索引处的字符

Ans = Subseq(0,0)

说明: -

1>

  if(S(i) == T(k))

           Subseq(i,k) = Subseq(i+1,k+1) + Subseq(i,k+1)

如果S(i)== T(k)那么

A>

索引k可能是T [k to end]中S [i to end]的子序列的一部分

因此,在T [k到end]中以S [i到end]的k开始的子序列的数量将等于T [k + 1到end]中S [i + 1到end]的子序列的数量

B个。

子序列可能不以k开头,在这种情况下我们需要评估S [i to end] in T [j + 1到结束]

结论:a.>& b.>生成完全不同的子序列,因此评估我们评估它们总和所需的所有可能子序列。

2>

else Subseq(i,k) = Subseq(i,k+1)

与案例1.>相反a.>这是不可能的,因为S(i)!= T(k)所以没有子序列可以开始 因此,我们只留下b.>可能性。

示例: -

S = "abc"  T = "aabc"

我们必须计算Subseq(0,0)

从上面的公式: -

1>

i = 0

k = 0

if(S(0)==T(0)) = true => Subseq(0,0) = Subseq(1,1) + Subseq(1,2)

现在我们要Subseq(1,1)& SUBSEQ(1,2)

2>

i = 1

k = 1

if(S(1)==T(1)) = false => Subseq(1,1) = Subseq(1,2)

正如你所看到的,Subseq(1,2)用于两个派生方程中,所以我只评估它一次

3>

i = 1

k = 2

if(S(1)==T(2)) = true => Subseq(1,2) = Subseq(2,3) + Subseq(1,3)

4.>

i = 1

k = 3

if(S(1)==T(3)) = false => Subseq(1,3) = Subseq(1,4)


as we know T(4) = null hence Subseq(1,4) = 0   hence Subseq(1,3) = 0

5>

i = 2

k = 3

 if(S(2)==T(3)) = true => Subseq(2,3) = Subseq(3,4) + Subseq(2,4)


    Subseq(3,4) = 1 as S(3) = null & S(4) == null and null string is always subsequence of null string

    Subseq(2,4) = 0 as T[end] = null & S[2 to end] ! = null so S[2 to end] is not subsequence of T[end]

    Subseq(2,3) = 1 + 0 = 1

6.>

使用5.>4.>以及3.>

Subseq(2,3) = 1

Subseq(1,3) = 0

Subseq(1,2) = Subseq(2,3) + Subseq(1,3)

Subseq(1,2) = 1 + 0 = 1

7.>

使用6.>2.>以及1.>

Subseq(1,2) = 1

Subseq(1,1) = Subseq(1,2) = 1

Subseq(0,0) = Subseq(1,1) + Subseq(1,2) = 2

<强>结论

Subseq(0,0) = 2 which means S="abc" appears 2 times as distinct subsequence in T = "aabc"

现在我们知道如何解决问题,我们可以更快地解决问题吗?

上述问题的答案是动态编程。

正如我们在上面的例子中看到的,我们使用Subseq(1,2)两次一次用于Subseq(1,1)&amp;一次

对于Subseq(0,0)所以如果我们只计算一次Subseq(1,2)并将其存储在

中它会很有用

表供以后使用。

所以DP建议我们预先计算当前层次的所有值

在评估当前问题之前的子问题,这样做我们可以防止还原剂

计算相同的子问题。

因此,在上面的例子中,我们可以评估Subseq(1,2)&amp;之前的子序列(2,3)并将其存储在

2-D表并在计算Subseq(0,0)时直接使用

现在出现的问题是最低层次的子问题?

在这种情况下,我们注意到方程: -

Subseq(i,k) = Subseq(i+1,k+1) + Subseq(i,k+1)

or

Subseq(i,k) = Subseq(i,k+1)

我们可以清楚地注意到每个问题(i,k)仅依赖于(i,k + 1)和(i + 1,k + 1)

所以i&amp; k取决于大于或等于它们的值。

使用上面的观察,我们可以从较高的值

开始计算2-d表(i,k) i&amp; j用于所有可能性,条目(0,0)将是问题的解决方案

伪代码: -

lenS = length(S)

lenT = length(T)

// Table to store subsequence count for all sub-problems 

Subseq[lenS+1][lenT+1];

// Empty string is subseq of Empty string 

Subseq[lenS][lenT] = 1

// NoN-Emtpy string is not subsequence of empty string

for(i = 0 ; i<lenS ; i++)
   Subseq[i][lenT] = 0


// Emtpy string is always subsequence of Non-empty string 

for(k = 0 ; k<lenT ; k++)
   Subseq[lenS][k] = 1


// Evaluate table from higher values to lower values

for(i = lenS-1 ; i>=0 ; i--) {


for(k = lenT-1 ; k>=0 ; k--) {



   if(S[i]==T[k]) 
       Subseq[i][k] = Subseq[i+1][k+1] + Subseq[i][k+1]

   else Subseq[i][k] = Subseq[i][k+1]         


}



}

// Answer

print Subseq[0][0]

注意:

在(i,k)的所有值的上述伪代码中,我们已经具有所需子问题的值

如果你没有得到任何上述解释,请发表评论

答案 1 :(得分:0)

我在java中找到了另一个更容易的解决方案。我们一次在字符串S 1字符中向后移动。在该特定示例中,DP阵列通过以下状态:100(最初) - > 110-> 120-> 122-> 124其中4是答案但是我不理解该解决方案:

    char[] T = "AABB".toCharArray();
    char[] S = "AB".toCharArray();

    int[] d = new int[S.length + 1];
    d[0] = 1;

    for (char c : T) {
        for (int i = S.length - 1; i >= 0; i--) {
            if (c == S[i]) {
                d[i + 1] += d[i];
            }
        }
    }

    System.out.println(d[S.length]);

答案 2 :(得分:0)

java实现是另一种解决问题的方法,实际上对我来说更有空间效率。

说明: -

d[i][k] = no of subsequence of S[0 to i] in T[0 to k]

d[i-1][k] = no of subsequnce of S[0 to i-1] in T[0 to k]

if(S[i]==T[k])
   d[i][k] = d[i][k-1] + d[i-1][k-1]

else d[i][k] = d[i][k-1]

上面是代码正在做的事情

如果你正确地注意到它,它与我的方法相同,但是以字符串的相反顺序