Leetcode115:不同的子序列

问题

给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。
一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)
示例 1:
输入: S = “rabbbit”, T = “rabbit”
输出: 3
解释:
如下图所示, 有 3 种可以从 S 中得到 “rabbit” 的方案。
(上箭头符号 ^ 表示选取的字母)
rabbbit
^^^^ ^^
rabbbit
^^ ^^^^
rabbbit
^^^ ^^^
示例 2:
输入: S = “babgbag”, T = “bag”
输出: 5
解释:
如下图所示, 有 5 种可以从 S 中得到 “bag” 的方案。
(上箭头符号 ^ 表示选取的字母)
babgbag
^^ ^
babgbag
^^ ^
babgbag
^ ^^
babgbag
^ ^^
babgbag
^^^

分析

总体来说,解动态规划题目的思路
两种解法

回溯法

很直观的想法,循环在字符串s中找与字符串t的第一个字符相匹配的字符;如果s的当前字符匹配,则递归s的子串和t的子串做同样的查找操作。
如,s=“babgbag” t=“bag”
循环在s中找与t中第一个字符,即b相匹配的字符;找到合适位置,即s的第0个字符,则递归:
s=“abgabg”,t=“ag”
递归终止条件:
如果t为“”,说明t已被匹配完成,计数增加,返回;
否则(t不为“”),如果s为“”,说明没有匹配完,不增加计数,返回。
代码见下。

动态规划

还是以s=“babgbag”,t=“bag”为例
借助一个二维数组dp[][];dp(i,j),表示s截止到第j个字符,能够匹配t.substring(0,i+1)的子序列的个数。
则有以下规则:

  • 如果t.charAt(i) == s.charAt(i),则:dp[i][j] = dp[i-1][j-1] + dp[i][j-1]
  • 如果t.charAt(i) != s.charAt(i),则:dp[i][j] = dp[i][j-1]

解释一下:
如果字符相同,判断s.substring(0,j+1),即为s_sub中有多少匹配t.substring(0,i+1),记为t_sub的子序列

  • 1)如果s.substring(0,j-1)与t.substring(0,i)有m个匹配的子序列,也就是s不包含第j个字符的时候,已经与t_sub有m个匹配的子序列了;又有t.charAt(i) == s.charAt(i),所以应该在加上多出这一个字符多出来的匹配结果,即2)(我写不明白了……)
  • 2)多出来的匹配结果,(不管为什么吧)就等于 dp[i-1][j-1]。
    代码见下。写的很丑。

代码

回溯法

	public int numDistinct(String s, String t) {
		// int ans = 0;
		helper(s, t);
		return ans;
	}
	
	private void helper(String s, String t){
		// 递归结束条件
		if(t.equals("")) {
			// 更新ans值
			ans ++;
			return ;
		}else if(s.equals("")){
			return ;
		}
		// 在s中找与t的第一个字符相匹配的字符;
		// 找到后,递归在s的子串找和t的子串匹配的字符
		char ch = t.charAt(0);
		for(int i = 0; i < s.length(); i++){
			if(s.charAt(i) == ch){
				helper(s.substring(i+1,s.length()), t.substring(1, t.length()));
			}
		}
		return ;
	}

动态规划

	public int numDistinct(String s, String t) {
		// 
		if(s==null || t==null || s.equals("") || t.equals("")) return 0;
		int[][] dp = new int[t.length()][s.length()];
		for(int i = 0; i < t.length(); i++){
			for(int j = 0; j < s.length(); j++){
				if(i == 0){
					//第一行单独处理
					if(j == 0) {
						if(s.charAt(j) == t.charAt(i)) dp[i][j] = 1;
					}else {
						if(s.charAt(j) == t.charAt(i)){
							dp[i][j] = dp[i][j-1] + 1;
						}else{
							dp[i][j] = dp[i][j-1] ;
						}
					}
					continue;
				}
				if(j < i){
					dp[i][j] = 0;
				}else{
					// 比较t.charAt(i)与s.charAt(j)是否相等
					if(t.charAt(i) == s.charAt(j)){
						dp[i][j] = dp[i-1][j-1] + dp[i][j-1];
					}else {
						dp[i][j] = dp[i][j-1];
					}
				}
	
			}
		}
		return dp[t.length()-1][s.length()-1];
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值