Idiot-maker

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

https://leetcode.com/problems/distinct-subsequences/

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

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).

Here is an example:
S = "rabbbit", T = "rabbit"

Return 3.

解题思路:

这再次是一道题意表述不清的题。按照题目的意思,是找出S中所有T的subsequence。这怎么做?其实题目的意思是,S中所有等于T的subsequence。

先想到dfs。搜索S,所有长度等于T的组合里,看有多少和T相等。代码不难,写出来后发现超时了。如下。

public class Solution {
    public int numDistinct(String S, String T) {
        List<String> result = new ArrayList<String>();
        dfs(T, S, new StringBuffer(), result, 0);
        return result.size();
    }
    
    public void dfs(String T, String S, StringBuffer current, List<String> result, int step) {
        if(current.length() > T.length()){
            return;
        }
        if(current.length() == T.length() && T.equals(current.toString())) {
            result.add(current.toString());
            return;
        }
        if(T.indexOf(current.toString()) != 0) {
            return;
        }
        for(int i = step; i < S.length(); i++) {
            current.append(S.charAt(i));
            dfs(T, S, current, result, i + 1);
            current.deleteCharAt(current.length() - 1);
        }
    }
}

于是参考了别人的解法。这是一道动态规划的题目,不过是二维的dp。动态规划的关键在于定义子状态,这里定义dp[i][j]为S[0..i]中的subsequence等于T[0..j]的数量。

如果S[i]!=T[j],S[0...i-1]的subsequence等于T[0..j]的有dp[i-1][j]个,那么在这个基础上加上S[i]也没用,所以dp[i][j]=dp[i-1][j]。

如果S[i]==T[j],那么在上面的基础上加上S[i]就可以了。所以S[0...i-1]的subsequence等于T[0..j-1]的有dp[i-1][j-1]个,因为S[i]==T[j],在这个基础上S加上S[i],T加上T[j],仍然可行,故首先有dp[i-1][j-1]个。还有一种情况,S不加上S[i],仍然拿S[0..i-1]和T[0..j]比,也就是和上面相同,这种情况下还有dp[i-1][j]个。其实,两种情况就分别是:不用S[i],以及用S[i]。因而,S[i]==T[j]的情况下,dp[i][j]=dp[i-1][j]+dp[i-1][j-1]。

递推方程出来了,动态规划还要解决初始值的问题。对于S=""的情况,也就是i==0,显然所有的dp=0。对于T==""的情况,也就是j==0,dp=1。因为S里的子串等于空的也就一种可能。

public class Solution {
    public int numDistinct(String S, String T) {
        int[][] dp = new int[S.length() + 1][T.length() + 1];
        for(int i = 0; i < T.length() + 1; i++) {
            dp[0][i] = 0;
        }
        for(int i = 0; i < S.length() + 1; i++) {
            dp[i][0] = 1;
        }
        for (int i = 1; i < S.length() + 1; i++) {
            for (int j = 1; j < T.length() + 1; j++) {
                if(S.charAt(i - 1) == T.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];
                } else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
        return dp[S.length()][T.length()];
    }
}

这还是比较复杂的一道动态规划,因为是二维,状态递推方程也略微复杂,需要细细体会。

update 2015/06/28:

更新了一个滚动数组的方法,因为原来的二维数组,本行的值仅仅依赖于上一行的值,所以实际上二维是不需要的。但是必须注意因为dp[j]依赖于dp[j-1],所以第二层循环必须从后往前。

public class Solution {
    public int numDistinct(String s, String t) {
        int[] dp = new int[t.length() + 1];
        dp[0] = 1;
        for(int i = 1; i <= s.length(); i++) {
            //因为dp[j]依赖于dp[j-1],所以本层循环必须从后往前
            for(int j = t.length(); j >= 1; j--) {
                // dp[j] = dp[j];
                if(s.charAt(i - 1) == t.charAt(j - 1)) {
                    dp[j] += dp[j - 1];
                }
            }
        }
        return dp[t.length()];
    }
}

 

posted on 2015-03-29 16:42  NickyYe  阅读(176)  评论(0编辑  收藏  举报