有些题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 ++;
}
}
}