[LeetCode] Distinct Subsequences

(Version 1.0)

这题在第一次做的时候走了一些弯路,问题还是出在了对于two sequence DP常用的二维DP的思路切入点不熟。熟悉的话应该很容易开始想到用一个二维数组dp中的元素dp[i][j]表示S.substring(i)中有多少个可以构成T.substring(j)的subsequence。状态转移关系其实是在举了一个例子一步步填表之后看出的规律,然后再从已经得到的规律往回推原因时明白了正确的思路是什么。代码如下:

 1 public class Solution {
 2     public int numDistinct(String S, String T) {
 3         if (S.length() < T.length()) {
 4             return 0;
 5         }
 6         int[][] dp = new int[S.length()][T.length()];
 7         dp[0][0] = S.charAt(0) == T.charAt(0) ? 1 : 0;
 8         for (int i = 1; i < dp.length; i++) {
 9             dp[i][0] = S.charAt(i) == T.charAt(0) ? dp[i - 1][0] + 1 : dp[i - 1][0];
10         }
11         for (int i = 1; i < dp.length; i++) {
12             char c = S.charAt(i);
13             for (int j = 1; j <= i && j < dp[0].length; j++) {
14                 dp[i][j] = (c == T.charAt(j)) ? dp[i - 1][j - 1] + dp[i - 1][j] : dp[i - 1][j];
15             }
16         }
17         return dp[dp.length - 1][dp[0].length - 1];
18     }
19 }

对于S.substring(i)的最后一个char S[i],若其与T.substring(j)中的最后一个char不同,说明向S.substring(i - 1)(这里不考虑i为0的情况,因为那是需要初始化dp数组时考虑的情况,且易得)后面append一个S[i]不会造成任何影响,所以在S[i] != T[j]时,dp[i][j] = dp[i - 1][j];但是如果S[i] == T[j],情况就有所不同了:我们可以选择在subsequence里面使用S[i]去match T[j],或者不使用S[i]去match T[j](append了S[i]和不append一样),也就是说维持S.substring(i - 1)的情况不变直接拿来用,所以此时的可能性数量就应该是两种情况的可能性数量之和,dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1]。这里第二项取dp[i - 1][j - 1]的原因是,既然确定了S[i]一定要match T[j],那么S.substring(i - 1)就一定只能match到T.substring(j - 1),这样才能把T[j]留给S[i]来match。

总体看来就是一道非常典型的two sequence DP,状态转移时需要取什么这个还是要花时间练习,正向推理出来正确的情况有点挑战。

原文地址:https://www.cnblogs.com/icecreamdeqinw/p/4328895.html