(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,状态转移时需要取什么这个还是要花时间练习,正向推理出来正确的情况有点挑战。