每天两道LeetCodeHard:(17)

235 阅读3分钟

115. Distinct Subsequences

动态规划它来咧!

题干:

Given a string S and a string T, count the number of distinct subsequences of S which equals T.

A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE" is a subsequence of "ABCDE" while "AEC" is not).

解释:

这个题让我们用S的子序列去构建T,返回可能的种类数。

思考:

还是那句话,看到字符串匹配,不要想dfs,就是dp来做。如果这个题是子串,那么dp ij一定是从左,左上,上,3个方向产生的,问题是现在要的是子序列,我们不妨来看一下以s中第i个字符结尾的子序列 等于T中第j个字符结尾的字串的种类,如果s i==t j,那么dp ij就等于s之前所有小于i的子序列能构成j-1的种类之和,也就是sum dp :j-1 如果s i!=t j,那么dp ij=0. 最后我们只要返回sum dp【:】【j】就行了.OJ通过了,但是时间效率是8%。 但是实际上这种方法还是落入了俗套,因为dp本质上就是研究当前位置和左,上,左上之间的关系。所以dp ij直接设定为所有的种类,如果当前字符能用,那么就等于用上这字符(左上)或者是不用上这个字符,直接用之前的结果即可,也就是以它结尾和不以它结尾。这样递推即可得到。 当然最好的办法还是直接画表,然后找关系就行。

答案:

class Solution {
    public int numDistinct(String s, String t) {
        //valid input
        int m=s.length();
        int n=t.length();
        if (m==0 || n==0){
            return 0;
        }
        int[][] dp = new int[m+1][n+1];
        //init
        for (int i =0;i<m;i++){
            if (s.charAt(i)==t.charAt(0)){
                dp[i][0]= 1;
            }
        }
        for (int i=1;i<m;i++ ){
            for (int j=1;j<n;j++){
                if (i<j){
                    dp[i][j]=0;
                    continue;
                }
                if (s.charAt(i)==t.charAt(j)){
                    dp[i][j]=sum(dp,i-1,j-1);
                }
                else{
                    dp[i][j]=0;
                }
            }
        }
        return sum(dp,m-1,n-1);
    }

    public int sum(int[][] dp, int end, int j){
        int count =0;
        for (int i=0;i<=end;i++){
            count+=dp[i][j];
        }
        return count;
    }
}

答案补充:

/**

  • @author li
  • @version 1.0
  • @date 2019-04-10 20:58
  • 思路:
  •  设dp[i][j]表示s[0:i-1]的子序列中t[0:j-1]出现的次数,则
    
  •  1.若s[i-1] == t[j-1] => dp[i][j] = dp[i-1][j-1] (用s[i-1]与t[j-1]配对) + dp[i-1][j](抛弃s[i-1],不用s[i-1]与t[j-1]配对)
    
  •  2.若s[i-1] != t[j-1] => dp[i-1][j] (直接抛弃s[i-1],不用s[i-1]与t[j-1]配对)
    

**/ class Solution { public int numDistinct(String s, String t) { if (s == null || s.length() == 0) { return 0; } int m = s.length(); int n = t.length();

    int[][] dp = new int[m + 1][n + 1];
    //初始化dp[i][0] = 1
    for (int i = 0; i <= m; i++) {
        dp[i][0] = 1;
    }
    
    //按t[0:0] => t[0:n]的顺序依次匹配
    for (int j = 1; j <= n; j++) {
        //因为当i<j时 s[0:i-1] 的长度比t[0:j-1]小,所以可以忽略
        for (int i = j; i <= m; i++) {
            if (s.charAt(i - 1) == t.charAt(j - 1)) {
                dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
            } else {
                dp[i][j] = dp[i - 1][j];
            } 
        }
    }
    return dp[m][n];
}

}