【Leetcode】115. Distinct Subsequences

又是一道DP的题目,个人感觉DP的题目比较难,主要因为:(1)DP的难点是寻找子问题,如果找到很好的子问题,那么就可以瞬间搞定。(2)通常也会带有一点backtracking的思想,有时候总是优先想到bt,然后思路就被限制了。

DP在leetcode主要解决两类问题,第一是求数目,第二是求最优解。少数可以解决所有解问题,不过一般而言所有解还是得用带memo的递归或者子问题。

在数组中出现的dp,一般是一个一维或者二维的数组,二维比较难想到。

题目:https://leetcode.com/problems/distinct-subsequences/?tab=Solutions


1.我首先想到了bt解法,思路就是不断删除s中的字符,直到长度等于t。有个注意点就是题目要求返回不同解,那么不同次序导致的解应该只返回一个,那么就需要加入一个start变量,来让后续的删除不出现在前面。虽然可以实现,但是、、、结果的超时还是无法避免。想过加memo,发现比较复杂,不能直接用string做索引,因为不同的删法可能会对应同一个string,但是结果不能相同。

	public int numDistinct(String s, String t) {
	  	return findNND(new StringBuffer(s), t, 0);
	}

	private int findNND(StringBuffer sb, String t, int start){
		if(sb.length() == t.length()) return sb.toString().equals(t)?1:0;
		int count = 0;
		for(int i = start; i < sb.length(); i++){
			char c = sb.charAt(i);
			sb.deleteCharAt(i);
			int r = findNND(sb, t, i);
			count += r;
			sb.insert(i, c);
		}
		return count;
	}
bt不带memo通常是指数级别或者阶乘,这里就是阶乘级别。


2.答案是一个时间n2,空间n2的dp解法。定义子问题为f(i,j):使用s中前j个字符所能得到t的前i个字符的数目。外层循环是i,表明想得到t中的前几个。内层循环遍历s。整个过程看的是i和j位置的字符是否相等。如果不等,那么当前的s的字符没用,用前j个s的结果和前j-1的结果一样,如果相等,那么当前s的字符可以使用,s前j-1包含t前i-1的数目就是使用s当前字符所得到的结果,还可以利用s前j-1的结果。这个解法主要时间复杂度很低。

	public int numDistinct(String s, String t) {
		int row = t.length();
		int col = s.length();
		int[][] dp = new int[row + 1][col + 1];
		Arrays.fill(dp[0], 1);
		for(int i = 1; i <= row; i++){
			for(int j = 1; j <= col; j++){
				dp[i][j] = dp[i][j - 1];
				if(t.charAt(i - 1) == s.charAt(j - 1))
					dp[i][j] += dp[i - 1][j - 1];
			}
		}
		return dp[row][col];
	}



今天又刷到一道类似的题目,运用了上述dp思路,题目要求找到两个string的公共最长subsequence长度。还是可以定义相同的子问题f(i,j),如果s的i位置和t的j位置相等,长度应该等于f(i-1,j-1)+1,否则等于f(i-1,j)和f(i,j-1)的最大值。这是和上题的一个不同点,上题的t是一个完整字符串,所以如果不等,只能取f(i,j-1),但是这里是子序列,不同。

题目:http://www.geeksforgeeks.org/dynamic-programming-set-4-longest-common-subsequence/

	public int LCS(String s, String t){
		int row = s.length();
		int col = t.length();
		int[][] dp = new int[row + 1][col + 1];
		for(int i = 1; i <= row; i++){
			for(int j = 1; j <= col; j++){
				if(s.charAt(i - 1) == t.charAt(j - 1))
					dp[i][j] = dp[i - 1][j - 1] + 1;
				else
					dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
			}
		}
		return dp[row][col];
	}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值