Distinct Subsequences

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()];

    }

}

 

你可能感兴趣的:(sequence)