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