Distinct Subsequences 在S中找T包含的所有字符的组合数@LeetCode

题目有些不好理解,实际上的意思就是在S中找T包含的所有字符(要按T的顺序),一共有几种组合?

动态规划。不会做,参考了

http://leetcodesolution.blogspot.com/2013/09/distinct-subsequences.html

http://blog.csdn.net/abcbc/article/details/8978146


特别是第一篇:


S  =  "rabbbit" T  =  "rabbit"
Return  3 .

Analysis:

If S[0] = T[0], then T is a subsequence of S if
  • the rest of string T is a subsequence the rest of string S,  or
  • T is a subsequence of the rest of string S

If S[0] != T[0], then T cannot be a subsequence of S; it could only be the subsequence  of the rest of S.

Let S'[i] be the suffix of S beginning from index i, and T'[j] be the suffix of T beginning from index j and f[i][j] is the number of subsequence of S'[i] and T'[j]. By the above analysis, we can get the transition function of DP as

  • f[i][j] = f[i+1][j+1] + f[i+1][j], if S[i] == T[j]
  • f[i][j] = f[i+1][j], if S[i] != T[j]

Now consider the end condition: if j reached the end of string T, that means, all chars in T has been satisfied in S  (from the transition function we  can notice that the j increased only when S[i] == T[j]), the subsequence has already been found in S. On the other hand, if the end of string S was reached, and end of string T was not, no subsequence was found.

  • f[i][j] =1, if j == T.size()
  • f[i][j] = 0, if I ==S.size() &&  j != T.size()


package Level4;


/**
 * 
 * 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.
 *
 */
public class S115 {

	public static void main(String[] args) {

	}
	
	public int numDistinct(String S, String T) {
        int m = S.length();
        int n = T.length();
        if(m==0 || n==0){
        	return 0;
        }
        
        int[][] dp = new int[m+1][n+1];
        for(int i=m; i>=0; i--){
        	for(int j=n; j>=0; j--){
        		if(j == n){		// 如果j已经到达T的末端,就说明T中所有的字符都在S中找到
        			dp[i][j] = 1;
        		}else if(i == m){	// 如果i到达S的末端,且j还没到达T的末端,则说明无法找到满足的subsequence
        			dp[i][j] = 0;
        		}else if(S.charAt(i) != T.charAt(j)){	// 如果当前比较的字符pair不同,则说明T肯定不会在以i开头的序列中,要有也只能在以i+1开头的子序列
        			dp[i][j] = dp[i+1][j];
        		}else{		// 如果当前比较的字符pair相同,则说明T可能在以i开头的序列中,也可能在以i+1开头的序列中
        			dp[i][j] = dp[i+1][j+1] + dp[i+1][j];
        		}
        	}
        }
        
        return dp[0][0];
    }

}



最好的解释:


遇到这种两个串的问题,很容易想到DP。但是这道题的递推关系不明显。可以先尝试做一个二维的表int[][] dp,用来记录匹配子序列的个数(以S ="rabbbit",T = "rabbit"为例):

    r a b b b i t

  1 1 1 1 1 1 1 1

0 1 1 1 1 1 1 1

a 0 1 1 1 1

b 0 0 2 3 3 3

b 0 0 0 0 3 3 3

i 0 0 0 0 0 0 3 3

t 0 0 0 0 0 0 0 3  

从这个表可以看出,无论T的字符与S的字符是否匹配,dp[i][j] = dp[i][j - 1].就是说,假设S已经匹配了j - 1个字符,得到匹配个数为dp[i][j - 1].现在无论S[j]是不是和T[i]匹配,匹配的个数至少是dp[i][j - 1]。除此之外,当S[j]和T[i]相等时,我们可以让S[j]和T[i]匹配,然后让S[j - 1]和T[i - 1]去匹配。所以递推关系为:

dp[0][0] = 1; // T和S都是空串.

dp[0][1 ... S.length() - 1] = 1; // T是空串,S只有一种子序列匹配。

dp[1 ... T.length() - 1][0] = 0; // S是空串,T不是空串,S没有子序列匹配。

dp[i][j] = dp[i][j - 1] + (T[i - 1] == S[j - 1] ? dp[i - 1][j - 1] : 0).1 <= i <= T.length(), 1 <= j <= S.length()






public class Solution {
    public int numDistinct(String S, String T) {
        int[][] cnt = new int[T.length()+1][S.length()+1];
        for(int j=0; j<S.length(); j++) {
            cnt[0][j] = 1;
        }
        for(int i=1; i<T.length(); i++) {
            cnt[i][0] = 0;
        }
        
        for(int i=1; i<=T.length(); i++) {
            for(int j=1; j<=S.length(); j++) {
                if(T.charAt(i-1) != S.charAt(j-1)) {
                    cnt[i][j] = cnt[i][j-1];    // The old way to match
                } else {    // Match, it allows us to have a new way to match
                    cnt[i][j] = cnt[i][j-1] + cnt[i-1][j-1];    // old way + new way
                }
            }
        }
        
        return cnt[T.length()][S.length()];
    }
}



附带递归解法:

  public int numDistinct(String S, String T) {
    // Start typing your Java solution below
    // DO NOT write main() function
    if (S.length() == 0) {
      return T.length() == 0 ? 1 : 0;
    }
    if (T.length() == 0) {
      return 1;
    }
    int cnt = 0;
    for (int i = 0; i < S.length(); i++) {
      if (S.charAt(i) == T.charAt(0)) {
        cnt += numDistinct(S.substring(i + 1), T.substring(1));
      }
    }
    return cnt;
  }


这道题可以作为两个字符串DP的典型:

两个字符串:

先创建二维数组存放答案,如解法数量。注意二维数组的长度要比原来字符串长度+1,因为要考虑第一个位置是空字符串。

然后考虑dp[i][j]和dp[i-1][j],dp[i][j-1],dp[i-1][j-1]的关系,如何通过判断S.charAt(i)和T.charAt(j)的是否相等来看看如果移除了最后两个字符,能不能把问题转化到子问题。

最后问题的答案就是dp[S.length()][T.length()]

还有就是要注意通过填表来找规律。


http://blog.csdn.net/abcbc/article/details/8978146

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值