LC Dynamic_Programming summary

有些题DP数组的意义很明显,有些则需要先写出递归的形式再转化成DP数组的方式

DP数组计算的方向与递归的方向相反,所以最重要的就是了解题目是怎么用递归实现的


相关的题目有:

10. Regular Expression Matching

Implement regular expression matching with support for '.' and '*'.

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

The function prototype should be:
bool isMatch(const char *s, const char *p)

Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true

先写递归(注意去掉重复的递归):

package l10;
/*
 * recursive
 * AC after removing duplicate recursion
 */
public class Recursive_AC {
    public boolean isMatch(String s, String p) {
        if("".equals(p))	return "".equals(s);
        if("".equals(s))	return p.length()>=2 && p.charAt(1)=='*' && isMatch(s, p.substring(2));
        
        if(p.length()>1 && p.charAt(1) == '*') {
        	if(p.charAt(0) == '.' || p.charAt(0) == s.charAt(0))
        		return isMatch(s.substring(1), p) || isMatch(s, p.substring(2));
        	else
        		return isMatch(s, p.substring(2));
        }
        
        return (p.charAt(0)=='.' || p.charAt(0) == s.charAt(0)) && isMatch(s.substring(1), p.substring(1));
    }
}

再根据递归反方向写DP

package l10;
/*
 * DP
 */
public class Solution {
    public boolean isMatch(String s, String p) {
    	boolean[][] dp = new boolean[1+s.length()][1+p.length()];
    	dp[s.length()][p.length()] = true;
    	for(int j=p.length()-2; j>=0; j-=2) {
    		if(p.charAt(j+1)=='*')
    			dp[s.length()][j] = true;
    		else
    			break;
    	}
    	
    	for(int i=s.length()-1; i>=0; i--) 
    		for(int j=p.length()-1; j>=0; j--) {
    			if(j != p.length()-1 && p.charAt(j+1) == '*') {
    				if(p.charAt(j) == '.' || p.charAt(j) == s.charAt(i))
    					dp[i][j] = dp[i+1][j] || dp[i][j+2] || dp[i+1][j+2];
    				else
    					dp[i][j] = dp[i][j+2];
    			} else if(p.charAt(j) == '.' || p.charAt(j) == s.charAt(i)) {
    				dp[i][j] = dp[i+1][j+1];
    			}
    		}
    	
    	return dp[0][0];
    }
}


72. Edit Distance

Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.)

You have the following 3 operations permitted on a word:

a) Insert a character
b) Delete a character
c) Replace a character

package l72;

import java.util.HashMap;
import java.util.Map;

/*
 * recursive
 */
public class recursive_TLE {
	
	Map<String, Integer> m = new HashMap<String, Integer>();
	
    public int minDistance(String s1, String s2) {
    	int cnt = 0;
    	while(cnt<s1.length() && cnt<s2.length() && s1.charAt(cnt)==s2.charAt(cnt))
    		cnt++;
    	
    	if(m.containsKey(s1 + "#" + s2))
    		return m.get(s1 + "#" + s2); // may be wrong if # exiets
    	
    	if(cnt == s1.length())	return s2.length()-cnt;
    	if(cnt == s2.length())	return s1.length()-cnt;
    	
    	int a1 = minDistance(s1.substring(cnt+1), s2.substring(cnt));
    	int a2 = minDistance(s1.substring(cnt), s2.substring(cnt+1));
    	int a3 = minDistance(s1.substring(cnt+1), s2.substring(cnt+1));
    	int ret = 1 + Math.min(a1, Math.min(a2, a3));
    	
    	m.put(s1 + "#" + s2, ret);
    	return ret;
    }
}

package l72;


/*
 * DP
 * 套路:
 * (1) 根据递归方向反方向求得DP方向
 * (2) 申请DP数组,根据递归pruning的条件初始化DP数组
 * (3) 根据递推公式求DP数组
 */
public class Solution {
	
    public int minDistance(String s1, String s2) {
    	int len1=s1.length(), len2=s2.length();
    	int[][] dp = new int[1+len1][1+len2];
    	
    	for(int i=0; i<=len1; i++)
    		dp[i][len2] = len1 - i;
    	for(int i=0; i<=len2; i++)
    		dp[len1][i] = len2 - i;
    	
    	for(int i=len1-1; i>=0; i--)
    		for(int j=len2-1; j>=0; j--)
    			if(s1.charAt(i) == s2.charAt(j))
    				dp[i][j] = dp[i+1][j+1];
    			else
    				dp[i][j] = 1 + Math.min(dp[i+1][j], Math.min(dp[i][j+1], dp[i+1][j+1]));
    	
    	return dp[0][0];
    }
}

87. Scramble String

Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively.

Below is one possible representation of s1 = "great":

    great
   /    \
  gr    eat
 / \    /  \
g   r  e   at
           / \
          a   t

To scramble the string, we may choose any non-leaf node and swap its two children.

For example, if we choose the node "gr" and swap its two children, it produces a scrambled string "rgeat".

    rgeat
   /    \
  rg    eat
 / \    /  \
r   g  e   at
           / \
          a   t

We say that "rgeat" is a scrambled string of "great".

Similarly, if we continue to swap the children of nodes "eat" and "at", it produces a scrambled string "rgtae".

    rgtae
   /    \
  rg    tae
 / \    /  \
r   g  ta  e
       / \
      t   a

We say that "rgtae" is a scrambled string of "great".

Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1.

package l87;

import java.util.HashSet;
import java.util.Set;

/*
 * memorization
 * 282/282:把ok删掉就AC,但是把notok删掉却不能AC
 */
public class Recursive_Memorization {
	Set<String> ok = new HashSet<String>();
	Set<String> notok = new HashSet<String>();
	
    public boolean isScramble(String s1, String s2) {
    	if(ok.contains(s1 + "#" + s2))	return true;
    	if(notok.contains(s1 + "#" + s2))	return false;
        int len1 = s1.length(), len2 = s2.length();
        if(len1 != len2)	return false;
        if(len1 == 1)	return s1.equals(s2);
        
        for(int i=1; i<len1; i++) {
        	String t11 = s1.substring(0, i), t12 = s1.substring(i);
        	String t21 = s2.substring(0, i), t22 = s2.substring(i);
        	if(isScramble(t11, t21) && isScramble(t12, t22)) {
        		ok.add(s1 + "#" + s2);
        		return true;
        	}
        	
        	String t23 = s2.substring(len2-i), t24 = s2.substring(0, len2-i);
        	if(isScramble(t11, t23) && isScramble(t12, t24)) {
        		ok.add(s1 + "#" + s2);
        		return true;
        	}
        }
        
        notok.add(s1 + "#" + s2);
        return false;
    }
}

package l87;


/*
 * DP
 * 这个DP怎么写???
 * 先考虑只能前面对前面dp1[i]
 * 再考虑只能从后面对前面dp2[i]
 * 但是还有个二叉树结构
 * 递归在做的是不断缩小String的length,所以DP先算length小的情况
 * 这样就想到了一个开3维数组DP的解法
 */
public class Solution {
	
    public boolean isScramble(String s1, String s2) {
        int len1 = s1.length(), len2 = s2.length();
        if(len1 != len2)	return false;
        
        char[] cs1=s1.toCharArray(), cs2=s2.toCharArray();
        boolean[][][] dp = new boolean[1+len1][len1][len1];
        for(int i=0; i<len1; i++)
        	for(int j=0; j<len1; j++)
        		dp[1][i][j] = (cs1[i]==cs2[j]);
        
        for(int i=2; i<=len1; i++)
        	for(int j=0; j<len1-i+1; j++)
        		for(int k=0; k<len1-i+1; k++)
        			for(int p=1; p<i; p++)
        				if((dp[p][j][k] && dp[i-p][j+p][k+p]) || (dp[p][j][k+i-p] && dp[i-p][j+p][k])) {
        					dp[i][j][k] = true;
        					break;
        				}
        
        return dp[len1][0][0];
    }
}

123. Best Time to Buy and Sell Stock III(应该还需要优化一些什么,就像132对回文判断的优化一样

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most two transactions.

Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

package l123;


/*
 * DP
 * dp[i][j]表示:从j位置开始最多还有i次机会的maxProfit
 * 
 * DP优化自后还不能过,简直了。。。。
 */
public class Solution {
	
	
    public int maxProfit(int[] prices) {
    	if(prices.length == 0)	return 0;
    	int c = 2, len = prices.length;
    	
    	// pre-cal helper array
    	int[][] helper = new int[len][len];
    	for(int i=0; i<len; i++) {
    		int max = 0, min = prices[i];
    		for(int j=i+1; j<len; j++) {
    			max = Math.max(max, prices[j]-min); 
	       		min = Math.min(min, prices[j]);
	       		helper[i][j] = max;
    		}
    	}
    			
    	
    	int[][] dp = new int[1+c][1+len];
    	for(int i=1; i<=c; i++)
    		for(int j=len-1; j>=0; j--) {
    			for(int k=j+1; k<len; k++) {
    				dp[i][j] = Math.max(dp[i][j], dp[i-1][k+1] + helper[j][k]);
    			}
    		}
    	
    	return dp[c][0];
    }
}

132. Palindrome Partitioning II

Given a string s, partition s such that every substring of the partition is a palindrome.

Return the minimum cuts needed for a palindrome partitioning of s.

For example, given s = "aab",
Return 1 since the palindrome partitioning ["aa","b"] could be produced using 1 cut.

package l132;

import java.util.Arrays;

/*
 * DP
 * 还需要对判断是不是回文进行一下优化
 */
public class Solution {
    public int minCut(String s) {
    	int len = s.length();
    	int[] dp = new int[len+1];
    	Arrays.fill(dp, s.length());
    	dp[len] = -1;
    	
    	char[] cs = s.toCharArray();
    	boolean[][] ok = new boolean[len][len];
    	for(int i=0; i<len; i++)
    		for(int j=0; j<len; j++) {
    			if(i-j < 0 || i+j >=len || cs[i+j]!=cs[i-j])
    				break;
    			ok[i-j][i+j] = true;
    		}
    	
    	for(int i=0; i<len; i++)
    		for(int j=1; j<len; j++) {
    			if(i-j+1 < 0 || i+j >=len || cs[i+j]!=cs[i-j+1])
    				break;
    			ok[i-j+1][i+j] = true;
    		}	
    	
    	for(int i=len-1; i>=0; i--)
    		for(int j=i+1; j<=len; j++)
    			if(ok[i][j-1])
	    			dp[i] = Math.min(dp[i], 1+dp[j]);
    		
        return dp[0];
    }


}

152. Maximum Product Subarray

Find the contiguous subarray within an array (containing at least one number) which has the largest product.

For example, given the array [2,3,-2,4],
the contiguous subarray [2,3] has the largest product = 6.

package l152;
/*
 * subarray:考虑必须以i位置结尾怎么样怎么样。。。。
 * 这里要记录最大 & 最小
 */
public class Solution {
    public int maxProduct(int[] nums) {
    	int len = nums.length;
    	if(len == 1)	return nums[0];
        int[] max = new int[len], min = new int[len];
        max[0] = nums[0]; min[0] = nums[0];
        int ret = nums[0];
        
        for(int i=1; i<len; i++) {
        	max[i] = Math.max(nums[i], Math.max(max[i-1]*nums[i], min[i-1]*nums[i]));
        	min[i] = Math.min(nums[i], Math.min(min[i-1]*nums[i], max[i-1]*nums[i]));
        	ret = Math.max(ret, max[i]);
        }
        
        return ret;
    }
}

213. House Robber II

Note: This is an extension of House Robber.

After robbing those houses on that street, the thief has found himself a new place for his thievery so that he will not get too much attention. This time, all houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, the security system for these houses remain the same as for those in the previous street.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

package l213;

import java.util.Arrays;

/*
 * 要做到:能把环形问题转化为非环形问题
 * dp[i]表示到第i为止的max(not include required)
 */
public class Solution {
    public int rob(int[] nums) {
    	if(nums.length == 0)	return 0;
    	if(nums.length == 1)	return nums[0];
        int[] dp = new int[nums.length];
        int ret = 0;
        
        // 偷第一个
        dp[0] = nums[0];
        dp[1] = nums[0];
        
        for(int i=2; i<nums.length-1; i++)
        	dp[i] = Math.max(nums[i] + dp[i-2], dp[i-1]);
        ret = dp[nums.length-2];
        
        // 不偷第一个
        Arrays.fill(dp, 0);
        dp[1] = nums[1];
        for(int i=2; i<nums.length; i++)
        	dp[i] = Math.max(nums[i] + dp[i-2], dp[i-1]);
        ret = Math.max(dp[nums.length-1], ret);
        
        return ret;
    }
}

279. Perfect Squares

Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n.

For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.

package l279;

import java.util.HashSet;
import java.util.Set;

/*
 * DP的思路很明显
 * 能不能先写个递归的版本?
 */
public class Solution {
    public int numSquares(int n) {
    	Set<Integer> s = new HashSet<Integer>();
    	for(int i=1; i<=n; i++) {
    		if(i*i > n)	break;
    		if(i*i == n)return 1;
    		s.add(i*i);
    	}
    	
        int[] dp = new int[1+n];
        for(int i : s)	dp[i] = 1;
        
        
        int cnt = 1;
        while(true) {
        	for(int i=1; i<=n; i++)
        		if(dp[i]==cnt) {
        			for(int j : s)
        				if(i+j<=n && dp[i+j]==0) {
        					dp[i+j] = cnt+1;
        					if(i+j == n)	return cnt+1;
        				}
        		}
        	cnt ++;
        }
        
    }
}








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值