动态规划总结

交叉字符串

给出三个字符串:s1、s2、s3,判断s3是否由s1和s2交叉构成。

您在真实的面试中是否遇到过这个题? 
Yes
样例

比如 s1 = "aabcc" s2 = "dbbca"

    - 当 s3 = "aadbbcbcac",返回  true.

    - 当 s3 = "aadbbbaccc", 返回 false.

public class Solution {
    /*
     * @param s1: A string
     * @param s2: A string
     * @param s3: A string
     * @return: Determine whether s3 is formed by interleaving of s1 and s2
     */
    public boolean isInterleave(String s1, String s2, String s3) {
        // write your code here
       if(s1.length() + s2.length() != s3.length()) return false;  
        boolean[][] res = new boolean[s1.length() + 1][s2.length() + 1];  
        res[0][0] = true;  
        for(int i = 1; i <= s1.length(); i++)   
            res[i][0] = res[i - 1][0] && s1.charAt(i - 1) == s3.charAt(i - 1);  
        for(int j = 1; j <= s2.length(); j++)   
            res[0][j] = res[0][j - 1] && s2.charAt(j - 1) == s3.charAt(j - 1);  
        for(int i = 1; i <= s1.length(); i++) {  
            for(int j = 1; j <= s2.length(); j++) {  
                if(s3.charAt(i + j - 1) == s1.charAt(i - 1) &&  
                s3.charAt(i + j - 1) == s2.charAt(j - 1)) {  
                    res[i][j] = res[i - 1][j] || res[i][j - 1];  
                } else if (s3.charAt(i + j - 1) == s1.charAt(i - 1)) {  
                    res[i][j] = res[i - 1][j];  
                } else if (s3.charAt(i + j - 1) == s2.charAt(j - 1)) {  
                    res[i][j] = res[i][j - 1];  
                } else   
                    res[i][j] = false;  
            }  
        }  
        return res[s1.length()][s2.length()];  
    }
}
最长上升子序列

给定一个整数序列,找到最长上升子序列(LIS),返回LIS的长度。

说明

最长上升子序列的定义:

最长上升子序列问题是在一个无序的给定序列中找到一个尽可能长的由低到高排列的子序列,这种子序列不一定是连续的或者唯一的。
https://en.wikipedia.org/wiki/Longest_increasing_subsequence

样例

给出 [5,4,1,2,3],LIS 是 [1,2,3],返回 3
给出 [4,2,4,5,3,7],LIS 是 [2,4,5,7],返回 4

public class Solution {
    /*
     * @param nums: An integer array
     * @return: The length of LIS (longest increasing subsequence)
     */
    //  子序列可以不连续,子串必须连续
    public int longestIncreasingSubsequence(int[] nums) {
        // write your code here
        if (nums == null || nums.length == 0) {
    		return 0;
    	}
    	int n = nums.length;
    	int[] dp  = new int[n];
    	int max = 0;
    	for (int i = 0; i < n; i++) {
    		dp[i] = 1;
    		for (int j = 0; j < i; j++) {
    			if (nums[j] < nums[i]){
    				dp[i] = dp[i] > dp[j] + 1 ? dp[i] : dp[j] + 1;
    			}
    		}
    		if (dp[i] > max) {
    		    max = dp[i];
    		}
    	}
    	return max;
    }
}
最长公共子序列

给出两个字符串,找到最长公共子序列(LCS),返回LCS的长度。

说明

最长公共子序列的定义:

  • 最长公共子序列问题是在一组序列(通常2个)中找到最长公共子序列(注意:不同于子串,LCS不需要是连续的子串)。该问题是典型的计算机科学问题,是文件差异比较程序的基础,在生物信息学中也有所应用。
  • https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
样例

给出"ABCD" 和 "EDCA",这个LCS是 "A" (或 D或C),返回1

给出 "ABCD" 和 "EACB",这个LCS是"AC"返回 2

public class Solution {
    /*
     * @param A: A string
     * @param B: A string
     * @return: The length of longest common subsequence of A and B
     */
    public int longestCommonSubsequence(String A, String B) {
        // write your code here
        // 文件进行差异比较的基础,生物信息比较
         if (A == null || B == null || A.length() == 0 || B.length() == 0) {
            return 0;
        }
        int[][] check = new int[A.length()  + 1][B.length() + 1];
        for (int i = 1; i <= A.length(); i++) {
            for (int j = 1; j <= B.length(); j++) {
                if (A.charAt(i - 1) == B.charAt(j - 1)) {
                    check[i][j] = check[i - 1][j - 1] + 1;
                } else {
                    check[i][j] = Math.max(check[i][j], check[i - 1][j]);
                    check[i][j] = Math.max(check[i][j], check[i][j - 1]);
                }
            }
        }
        return check[A.length()][B.length()];
    }
}
最小调整代价
public class Solution {
    /*
     * @param A: An integer array
     * @param target: An integer
     * @return: An integer
     */
    public int MinAdjustmentCost(List<Integer> A, int target) {
        // write your code here
          // write your code here  
        if(A.size()<2)  {  
            return 0;  
        }  
        int m = A.size();  
        long [][]dp = new long[m][101];  
        int i = 0, j = 0;  
        for (i = 0; i < 101; i++) {  
            dp[0][i] = Math.abs(A.get(0) - i);   
        }  
        for (i = 1; i < m; i++) {  
            for (j = 0; j < 101; j++) {  
                dp[i][j] = Integer.MAX_VALUE;    
                int dif = Math.abs(j - A.get(i));  
                int max = Math.min(100, j + target);  
                int min = Math.max(0, j - target);  
                for (int k = min; k <= max; k++) {  
                    dp[i][j] = Math.min(dp[i][j], dp[i - 1][k] + dif);  
                }  
            }  
        }  
        long ans = Integer.MAX_VALUE;  
        for (j = 0; j < 101; j++) {  
            ans = Math.min(ans, dp[m - 1][j]);  
        }  
        return(int) ans;  
    }
}
单词拆分1

给出一个字符串s和一个词典,判断字符串s是否可以被空格切分成一个或多个出现在字典中的单词。

样例

给出

s = "lintcode"

dict = ["lint","code"]

返回 true 因为"lintcode"可以被空格切分成"lint code"

public class Solution {
    /*
     * @param s: A string
     * @param dict: A dictionary of words dict
     * @return: A boolean
     */
    public boolean wordBreak(String s, Set<String> dict) {
        // write your code 
        if (s == null || dict.contains(s)) {
            return true;
        }

        boolean[] valid = new boolean[s.length() + 1];
        valid[s.length()] = true;
        int maxLength = calMaxLength(dict);
        for (int i = s.length() - 1; i >= 0; i--) {
            for (int j = i; j < s.length() && (i - j) <= maxLength; j++) {//iterate [0 ~ i]
                if (valid[j + 1] && dict.contains(s.substring(i, j + 1))) {
                    valid[i] = true;
                    break;
                }
            }
        }
        return valid[0];
    }

    public int calMaxLength(Set<String> dict) {
        int length = 0;
        for (String word : dict) {
            length = Math.max(length, word.length());
        }
        return length;
    
    }
}
分割回文串2

给定一个字符串s,将s分割成一些子串,使每个子串都是回文。

返回s符合要求的的最少分割次数。

样例

比如,给出字符串s = "aab"

返回 1, 因为进行一次分割可以将字符串s分割成["aa","b"]这样两个回文子串

public class Solution {
    /**
     * @param s: A string
     * @return: An integer
     */
    public int minCut(String s) {
        // write your code here
        boolean[][] b = new boolean[s.length()][s.length()];
        for (int i = 1; i <= s.length(); i++) {
            //j表示以i为中心的子字符串左侧
            //s.length() - i + 1,计算的是i的右侧还有几个字符
            //所以j在[0, s.length() - i + 1)
            //这里便能解释为什么i必须从1开始,如果从0开始的话,0是没有左侧的
            for (int j = 0; j < s.length() - i + 1; j++) {
                //以i为中心,与j对应的右侧位置k
                int k = j + i - 1;
                if (i == 1) {
                    b[j][k] = true;
                } else {
                    if (s.charAt(j) == s.charAt(k) && (j == k - 1 || b[j + 1][k - 1]))
                        b[j][k] = true;
                }
            }
        }
        int[] dp = new int[s.length()];
        Arrays.fill(dp, Integer.MAX_VALUE);
        for (int i = 0; i < s.length(); i++) {
            if (b[0][i]) {
                dp[i] = 0;
            }
            for (int j = 0; j < i; j++) {
                if (b[j + 1][i])
                    dp[i] = Math.min(dp[i], dp[j] + 1);
            }
        }
        return dp[s.length() - 1];

// 作者:琼珶和予
// 链接:https://www.jianshu.com/p/188780c84d78
// 來源:简书
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    }
}
数字三角形

给定一个数字三角形,找到从顶部到底部的最小路径和。每一步可以移动到下面一行的相邻数字上。

 注意事项

如果你只用额外空间复杂度O(n)的条件下完成可以获得加分,其中n是数字三角形的总行数。

样例

比如,给出下列数字三角形:

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

从顶到底部的最小路径和为11 ( 2 + 3 + 5 + 1 = 11)。

public class Solution {
    /*
     * @param triangle: a list of lists of integers
     * @return: An integer, minimum path sum
     */
    public int minimumTotal(int[][] triangle) {
        // write your code here
       if (triangle == null || triangle.length== 0) {
            return 0;
        }
        int n = triangle.length;
        int[][] sum = new int[n][n];
        for (int i = 0; i < n; i++) {
            sum[n - 1][i] = triangle[n - 1][i];
        }
        for (int i = n - 2; i >= 0; i--) {
            for (int j = 0; j <= i; j++) {
                sum[i][j] = Math.min(sum[i + 1][j], sum[i + 1][j + 1]) + triangle[i][j];
            }
        }
        return sum[0][0];
    }
}
最小路径和

给定一个只含非负整数的m*n网格,找到一条从左上角到右下角的可以使数字和最小的路径。


 注意事项

你在同一时间只能向下或者向右移动一步

public class Solution {
    /*
     * @param grid: a list of lists of integers
     * @return: An integer, minimizes the sum of all numbers along its path
     */
    public int minPathSum(int[][] grid) {
        // write your code here
         if (grid == null || grid.length == 0 || grid[0].length == 0) {
            return 0;
        }
        int row = grid.length;
        int col = grid[0].length;
        int[][] matrix = new int[row][col];
        matrix[0][0] = grid[0][0];
        //Add up for 1st row && 1st col
        for (int i = 1; i < row; i++) {
            matrix[i][0] = matrix[i - 1][0] + grid[i][0];
        }
        for (int j = 1; j < col; j++) {
            matrix[0][j] = matrix[0][j - 1] + grid[0][j];
        }
        //Evaluate
        for (int i = 1; i < row; i++) {
            for (int j = 1; j < col; j++) {
                matrix[i][j] = Math.min(matrix[i - 1][j], matrix[i][j - 1])
                                + grid[i][j];
            }
        }
        return matrix[row - 1][col - 1];
    }
}
不同的路径

有一个机器人的位于一个 m × n 个网格左上角。

机器人每一时刻只能向下或者向右移动一步。机器人试图达到网格的右下角。

问有多少条不同的路径?

 注意事项

n和m均不超过100

样例

给出 m = 3 和 n = 3, 返回 6.
给出 m = 4 和 n = 5, 返回 35.

public class Solution {
    /**
     * @param m: positive integer (1 <= m <= 100)
     * @param n: positive integer (1 <= n <= 100)
     * @return: An integer
     */
    public int uniquePaths(int m, int n) {
        // write your code here
         int dp[] = new int[n];
        for(int i = 0; i < n; i++) {
            dp[i] = 1;
        }
        for(int i = 1; i < m; i++) {
            for(int j = 1; j < n; j++) {
                dp[j] += dp[j-1];
            }
        }
        return dp[n-1];
    }
}
不同的路径2

"不同的路径" 的跟进问题:

现在考虑网格中有障碍物,那样将会有多少条不同的路径?

网格中的障碍和空位置分别用 1 和 0 来表示。

 注意事项

m 和 n 均不超过100

样例

如下所示在3x3的网格中有一个障碍物:

[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]

一共有2条不同的路径从左上角到右下角。

public class Solution {
    /**
     * @param obstacleGrid: A list of lists of integers
     * @return: An integer
     */
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        // write your code here
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[] dp = new int[n];
        boolean row = false;//第一行是否有障碍
        boolean column = false;//第一列是否有障碍
        for(int i = 0; i < n; i++){//第一步动态规划
            if(obstacleGrid[0][i] == 1){
                row = true;
            }
            if(obstacleGrid[0][i] == 0 && row == false)
                dp[i] = 1;
            else
                dp[i] = 0;
        }
        if(obstacleGrid[0][0] == 1){
            column = true;
        }
        for(int i = 1; i < m; i++) {//其余步动归
            for(int j = 0; j < n; j++) {
                if(j == 0) {
                    if(obstacleGrid[i][0] == 1){
                        column = true;
                    }
                    if(obstacleGrid[i][0] == 0 && column == false)
                        dp[0] = 1;
                    else
                        dp[0] = 0;
                }else{
                    if(obstacleGrid[i][j] == 1)
                        dp[j] = 0;
                    else
                        dp[j] += dp[j-1];
                }

            }
        }
        return dp[n-1];
    }
}
跳跃游戏

给出一个非负整数数组,你最初定位在数组的第一个位置。   

数组中的每个元素代表你在那个位置可以跳跃的最大长度。    

判断你是否能到达数组的最后一个位置。

 注意事项

这个问题有两个方法,一个是贪心和 动态规划

贪心方法时间复杂度为O(N)

动态规划方法的时间复杂度为为O(n^2)

我们手动设置小型数据集,使大家可以通过测试的两种方式。这仅仅是为了让大家学会如何使用动态规划的方式解决此问题。如果您用动态规划的方式完成它,你可以尝试贪心法,以使其再次通过一次。

样例

A = [2,3,1,1,4],返回 true.

A = [3,2,1,0,4],返回 false.

public class Solution {
    /*
     * @param A: A list of integers
     * @return: A boolean
     */
    public boolean canJump(int[] A) {
        // write your code here
          if (A == null || A.length == 0) {
            return false;
        }
    //By default, boolean[] can is all false
        boolean[] can = new boolean[A.length];
        can[0] = true;
        for (int i = 1; i < A.length; i++) {
            for (int j = 0; j < i; j++) {
                if (can[j] && (j + A[j] >= i)) {
                    can[i] = true;
                    break;
                }
            }
        }
        return can[A.length - 1];
    }
}
不同的子序列

给出字符串S和字符串T,计算S的不同的子序列中T出现的个数。

子序列字符串是原始字符串通过删除一些(或零个)产生的一个新的字符串,并且对剩下的字符的相对位置没有影响。(比如,“ACE”“ABCDE”的子序列字符串,而“AEC”不是)。 

样例

给出S = "rabbbit", T = "rabbit"

public class Solution {
    /*
     * @param : A string
     * @param : A string
     * @return: Count the number of distinct subsequences
     */
    public int numDistinct(String S, String T) {
        // write your code here
         if(null == S || null == T)  return 0;
        // dp 表示S中的前i个字符包含T的前j个字符的个数
        int[][] dp = new int[S.length() + 1][T.length() + 1];
        //1.先初始化
        for(int i = 0;i <= S.length();i++)
        {
            dp[i][0] = 1;
        }
        for(int i = 1;i <= S.length();i++)
        {
            for(int j = 1;j <= T.length();j++)
            {
                //不相同
                if(S.charAt(i - 1) != T.charAt(j - 1))
                {
                    // 不包含S中的第i个
                    dp[i][j] = dp[i - 1][j];
                }
                else//相同
                {
                    // dp思想,匹配结果分为包含S中的第i个与不包含第i个两种
                    dp[i][j] = dp[i - 1][j - 1] +  dp[i - 1][j];
                }
            }
        }        
        return dp[S.length()][T.length()];
    }
};
编辑距离

给出两个单词word1和word2,计算出将word1 转换为word2的最少操作次数。

你总共三种操作方法:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

样例

给出 work1="mart" 和 work2="karma"

public class Solution {
    /*
     * @param word1: A string
     * @param word2: A string
     * @return: The minimum number of steps.
     */
    public int minDistance(String word1, String word2) {
        // write your code here
        if (word1 == null && word2 != null) {
			return word2.length();
		} else if (word1 != null && word2 == null) {
			return word1.length();
		} else if (word1 == null && word2 == null) {
			return 0;
		}
		int[][] DP = new int[word1.length() + 1][word2.length() + 1];
		for (int i = 1; i <= word1.length(); i++) {
			DP[i][0] = i;
		}
		for (int j = 1; j <= word2.length(); j++) {
			DP[0][j] = j;
		}

		for (int i = 1; i <= word1.length(); i++) {
			for (int j = 1; j <= word2.length(); j++) {
				DP[i][j] = Math.min(Math.min(DP[i - 1][j] + 1, DP[i][j - 1] + 1), word1.charAt(i - 1) == word2.charAt(j - 1) ? DP[i - 1][j - 1] : DP[i - 1][j - 1] + 1);
			}
		}

		return DP[word1.length()][word2.length()];
    }
}
不同的二叉树

给出 n,问由 1...n 为节点组成的不同的二叉查找树有多少种?

样例

给出n = 3,有5种不同形态的二叉查找树:

1           3    3       2      1
 \         /    /       / \      \
  3      2     1       1   3      2
 /      /       \                  \
2     1          2                  3
public class Solution {
    /*
     * @param n: An integer
     * @return: An integer
     */
    public int numTrees(int n) {
        // write your code here
        int[] dp = new int[n + 1];
       return helper(n, dp);
    }
    
    public int helper(int n, int[] dp) {
        if (n <= 1) {
            return 1;
        }
        if (dp[n] != 0 ) {
            return dp[n];
        }
        int total = 0;
        for (int i = 1; i <= n; i++) {
            int left = helper(i - 1, dp);
            int right = helper(n - i, dp);
            total += (left * right);
        }
        
        dp[n] = total;
        
        return total;
    }
}
不同的二叉树2

给出n,生成所有由1...n为节点组成的不同的二叉查找树

样例

给出n = 3,生成所有5种不同形态的二叉查找树:

1         3     3       2    1
 \       /     /       / \    \
  3     2     1       1   3    2
 /     /       \                \
2     1         2                3
/**
 * Definition of TreeNode:
 * public class TreeNode {
 *     public int val;
 *     public TreeNode left, right;
 *     public TreeNode(int val) {
 *         this.val = val;
 *         this.left = this.right = null;
 *     }
 * }
 * 返回二叉树
 */
public class Solution {
    /**
     * @paramn n: An integer
     * @return: A list of root
     */
    public List<TreeNode> generateTrees(int n) {
        // write your code here
              // write your code here
        return helper(1, n);
    }
    
    public List<TreeNode> helper(int start, int end) {
        List<TreeNode> res = new ArrayList<TreeNode>();
        if (start > end) {
            res.add(null);
            return res;
        }
        
        for (int i = start; i <= end; i++) {
            List<TreeNode> lefts = helper(start, i - 1);
            List<TreeNode> rights = helper(i + 1, end);
            for (TreeNode l : lefts) {
                for (TreeNode r : rights) {
                    TreeNode root = new TreeNode(i);
                    root.left = l;
                    root.right = r;
                    res.add(root);
                }
            }
        }
        return res;
    }
}
乘积最大子序列

找出一个序列中乘积最大的连续子序列(至少包含一个数)。

样例

比如, 序列 [2,3,-2,4] 中乘积最大的子序列为 [2,3] ,其乘积为6

public class Solution {
    /*
     * @param nums: An array of integers
     * @return: An integer
     Time Limit Exceeded
   int max = nums[0];
        int index = 1;
        
        while(index <= nums.length){
            
            for(int i=0;i<=nums.length-index;i++){
                int product = 1;
                for(int j=0;j<index;j++){
                    product *= nums[i+j];
                }
                
                if(product > max)
                     max = product;
            }
            index ++;
        }
        return max;
    }
     */
    public int maxProduct(int[] nums) {
        // write your code here
         int posmax=nums[0],negmax=nums[0],max=nums[0];
       
       for(int i=1;i<nums.length;i++){
           int tempPosMax = posmax;
           int tempNegMax = negmax;
           posmax = Math.max(nums[i],Math.max(nums[i]*tempPosMax,nums[i]*tempNegMax));
           negmax = Math.min(nums[i],Math.min(nums[i]*tempPosMax,nums[i]*tempNegMax));
           if(Math.max(posmax,negmax) > max){
               max = Math.max(posmax,negmax);
           }
       }
       
       return max;
    }
}


 
打劫房屋

假设你是一个专业的窃贼,准备沿着一条街打劫房屋。每个房子都存放着特定金额的钱。你面临的唯一约束条件是:相邻的房子装着相互联系的防盗系统,且 当相邻的两个房子同一天被打劫时,该系统会自动报警

给定一个非负整数列表,表示每个房子中存放的钱, 算一算,如果今晚去打劫,你最多可以得到多少钱 在不触动报警装置的情况下

样例

给定 [3, 8, 4], 返回 8.

public class Solution {
    /*
     * @param A: An array of non-negative integers
     * @return: The maximum amount of money you can rob tonight
     最基本的dp。      
看前一个或前两个的情况,再总和考虑当下的。      
思考的适合搞清楚当下的和之前的情况的关系。    
滚动数组的优化,就是确定了是这类“只和前一两个位子“相关的Fn而推出的。 
     */
    public long houseRobber(int[] A) {
        // write your code here
      int len=A.length;  
      if(A==null||len==0)  
      return 0;  
      long res[]=new long[len];  
      //res[i]表示打劫前i个房子获得的最大金额  
      for(int i=0;i<len;i++){  
          if(i==0)  
          res[i]=A[0];  
          if(i==1)  
          res[i]=Math.max(A[0],A[1]);  
          else if(i>1)  
          res[i]=Math.max(res[i-1],res[i-2]+A[i]);  
      }  
      return res[len-1];  
    }
}
最长上升子序列

给定一个整数数组(下标从 0 到 n-1, n 表示整个数组的规模),请找出该数组中的最长上升连续子序列。(最长上升连续子序列可以定义为从右到左或从左到右的序列。)

 注意事项

time

样例

给定 [5, 4, 2, 1, 3], 其最长上升连续子序列(LICS)为 [5, 4, 2, 1], 返回 4.

给定 [5, 1, 2, 3, 4], 其最长上升连续子序列(LICS)为 [1, 2, 3, 4], 返回 4.

public class Solution {
    /*
     * @param A: An array of Integer
     * @return: an integer
     O(n)跑2遍for.
O(1)是用了两个int来存:每次到i点时,i点满足条件或不满足条件所有的longestIncreasingContinuousSubsequence.
特点:返跑一回,ans还是继续和left轮的ans作比较;求的所有情况的最大值嘛。
     */
    public int longestIncreasingContinuousSubsequence(int[] A) {
        // write your code here
          if (A == null || A.length == 0) {
            return 0;
        }
        int leftRun = 1;
        int rightRun = 1;
        int ans = 1;
        for (int i = 1; i < A.length; i++) {
            if (A[i] > A[i - 1]) {
                leftRun++;
            } else {
                leftRun = 1;
            }
            ans = Math.max(ans, leftRun);
        }
        for (int i = A.length - 2; i >= 0; i--) {
            if (A[i] > A[i + 1]) {
                rightRun++;
            } else {
                rightRun = 1;
            }
            ans = Math.max(ans, rightRun);
        }
        return ans;
    }
}

最大平方

在一个二维01矩阵中找到全为1的最大正方形

样例
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

返回 4

public class Solution {
    /*
     * @param matrix: a matrix of 0 and 1
     * @return: an integer
     */
    public int maxSquare(int[][] matrix) {
        // write your code here
         if(matrix==null)
            return 0;
        int m = matrix.length;
        if(m ==0 )
            return 0;
        int n = matrix[0].length;
        if( n==0 )
            return 0;
        int res = -1;
        for(int i = 1;i<m;i++){
            for(int j = 1;j<n;j++){
                if(matrix[i][j]!=0 ){
                    matrix[i][j] = min3(matrix[i-1][j-1],matrix[i][j-1],matrix[i-1][j]) + 1;
                }
                res = Math.max(res,matrix[i][j]);
                
            }
        }
        return res*res;
    }
    public int min3(int a ,int b,int c){
        a = Math.min(a,b);
        c = Math.min(a,c);
        return c;
    }
}
解码游戏

有一个消息包含A-Z通过以下规则编码

'A' -> 1
'B' -> 2
...
'Z' -> 26

现在给你一个加密过后的消息,问有几种解码的方式

样例

给你的消息为12,有两种方式解码 AB(12) 或者 L(12). 所以返回 2

public class Solution {
    /*
     * @param s: a string,  encoded message
     * @return: an integer, the number of ways decoding
     */
    public int numDecodings(String s) {
        // write your code here
        // http://blog.youkuaiyun.com/Tri_Color_Flag/article/details/53375300
         if (s == null || s.length() == 0)
        {
            return 0;
        }
        int[] dp = new int[s.length() + 1];
        dp[0] = 1;
        for (int i = 1; i < dp.length; ++i) 
        {
            dp[i] = (s.charAt(i - 1) == '0') ? 0 : dp[i - 1];
            if (i > 1 && (s.charAt(i - 2) == '1' || (s.charAt(i - 2) == '2' && s.charAt(i - 1) <= '6'))) 
            {
                dp[i] += dp[i - 2];
            }
        }
        return dp[s.length()];
    }
}
完美平方

给一个正整数 n, 找到若干个完全平方数(比如1, 4, 9, ... )使得他们的和等于 n。你需要让平方数的个数最少。

样例

给出 n = 12, 返回 3 因为 12 = 4 + 4 + 4
给出 n = 13, 返回 2 因为 13 = 4 + 9

public class Solution {
    /*
     * @param n: a positive integer
     * @return: An integer
     
     一开始没clue.看了一下提示。

1. 第一步想到了,从数学角度,可能是从最大的perfect square number开始算起。
2. 然后想法到了dp, 假设最后一步用了最大的maxSqrNum, 那么就在剩下的 dp[i - maxSqrNum^2] +1 不就好了?
3. 做了,发现有个问题...最后一步选不选maxSqrNum?  比如12就是个例子。
	然后就根据提示,想到BFS。顺的。 把1~maxSqrNum 都试一试。找个最小的。
	看我把12拆分的那个example. 那很形象的就是BFS了。
	面试时候,如果拆分到这个阶段不确定,那跟面试官陶瓷一下,说不定也就提示BFS了。
     */
    public int numSquares(int n) {
        // write your code here
          if (n <= 0) {
        	return 0;
        }
        int[] dp = new int[n + 1];
        dp[0] = 0;

        for (int i = 1; i <= n; i++) {
        	int maxSqrNum = (int)Math.floor(Math.sqrt(i));
        	int min = Integer.MAX_VALUE;
        	for (int j = 1; j <= maxSqrNum; j++) {
        		min = Math.min(min, dp[i - j * j] + 1);
        	}
        	dp[i] = min;
        }
        return dp[n];
    }
}
打劫房屋2
 

在上次打劫完一条街道之后,窃贼又发现了一个新的可以打劫的地方,但这次所有的房子围成了一个圈,这就意味着第一间房子和最后一间房子是挨着的。每个房子都存放着特定金额的钱。你面临的唯一约束条件是:相邻的房子装着相互联系的防盗系统,且 当相邻的两个房子同一天被打劫时,该系统会自动报警

给定一个非负整数列表,表示每个房子中存放的钱, 算一算,如果今晚去打劫,你最多可以得到多少钱 在不触动报警装置的情况下。

 注意事项

这题是House Robber的扩展,只不过是由直线变成了圈

样例

给出nums = [3,6,4], 返回 6, 你不能打劫34所在的房间,因为它们围成一个圈,是相邻的.

public class Solution {
    /*
     * @param nums: An array of non-negative integers.
     * @return: The maximum amount of money you can rob tonight
     和House Robber I 类似,  DP.

根据dp[i-1]是否被rob来讨论dp[i]: dp[i] = Math.max(dp[i-1], dp[i - 2] + nums[i - 1]);

特别的是,末尾的last house 和 first house相连。这里就需要分别讨论两种情况:    
1. 最后一个房子被rob    
2. 最后一个房子没被rob   

两种情况做完,综合对比一下.
     */
    public int houseRobber2(int[] nums) {
        // write your code here
        if (nums == null || nums.length == 0) {
        	return 0;
        } else if (nums.length == 1) {
        	return nums[0];
        } else if (nums.length == 2) {
            return Math.max(nums[0], nums[1]);
        }

        int n = nums.length;

        //Last house not robbed
        int[] dp1 = new int[n];
        dp1[0] = nums[0];
        dp1[1] = Math.max(nums[0], nums[1]);
        for (int i = 2; i < n - 1; i++) {
        	dp1[i] = Math.max(dp1[i - 1], dp1[i - 2] + nums[i]);
        }
        dp1[n - 1] = dp1[n - 2];

        //Last house robbed
        int[] dp2 = new int[n];
        dp2[0] = 0;
        dp2[1] = nums[1];
        for (int i = 2; i < n - 2; i++) {
        	dp2[i] = Math.max(dp2[i - 1], dp2[i - 2] + nums[i]);
        }
        dp2[n - 1] = dp2[n - 3] + nums[n - 1];

        //Compare
        return Math.max(dp2[n - 1], dp1[n - 1]);
    }
}
最大整除子集

给一个由 无重复的正整数 组成的集合,找出满足任意两个元素 (Si, Sj) 都有 Si % Sj = 0 或 Sj % Si = 0 成立的最大子集

 注意事项

如果有多种解集,返回其中任意一个。

样例

给一个数组 [1,2,3],返回 [1,2] 或 [1,3]
给一个数组 [1,2,4,8],返回 [1,2,4,8]

public class Solution {
    /*
     * @param nums: a set of distinct positive integers
     * @return: the largest subset 
     */
    public List<Integer> largestDivisibleSubset(int[] nums) {
        // write your code here
        if (nums.length == 1) return nums[0];
        int max = nums[nums.length - 1];
        for (int i = nums.length - 2; i >= 0; i--) {
            nums[i] = Math.max(nums[i], nums[i] + nums[i + 1]);
            max = Math.max(max, nums[i]);
        }
        return max;
    }
}
返回最长回文子串不是返回长度





















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值