Number of Ways

Combination Sum IV

Given an integer array with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.

nums = [1, 2, 3] target = 4

思路

看到这个number of possible combinations 就应该想到这一题应该是动态规划。

State: 和输入的数组无关,而是用target的值来作为一个维度,所以这是一个 背包问题。 f[i] 组成的target为i的时候可能有多少种解。

Function: f[i] = Sum{f[i - A[j]], j = 0 ~ n && i - A[j] > 0}

Init: f[0] = 1

Answer: f[target]

Code

public class Solution {
    public int combinationSum4(int[] nums, int target) {
        if(nums == null || nums.length == 0) {
            return 0;
        }
        Arrays.sort(nums);
        int[] dp = new int[target + 1];

        for(int i = 1; i <= target; i++) {
            for(int j = 0; j < nums.length; j++) {
                if(i - nums[j] > 0) {
                    dp[i] += dp[i - nums[j]];
                } else if (i == nums[j]){
                    dp[i] += 1; // 一个答案可能有多种可能
                } else {
                    break;
                }
            }
        }       
        return dp[target];
    }
}

思路2

主要在于这题和之前的 combination sum 还不太一样,它把 [1,3] 和 [3,1] 这种重复搜索算成两个解。于是强行制造了 overlap subproblems. 于是这题用搜索解会 TLE,加个 hashmap 做记忆化搜索,立刻就过了。

O(n^d),d代表得到 >= target 的搜索深度。

public int combinationSum(int[] nums, int target) {
    return dfs(nums, 0, target, new HashMap<Integer, Integer>());
}

public int dfs(int[] nums, int curNum, int target, HashMap<Integer, Integer> map) {
    // boundary
    if(curNum > target) return 0;
    // base case
    if(curNum == target) return 1;
    // find in the cache first
    // curNum has the number of ways to add to the target
    if(map.containsKey(curNum)) return map.get(curNum);

    int count = 0;

    for(int i = 0; i < nums.length; i++) {
        count += dfs(nums, curNum + nums[i], target, map);
    }

    map.put(curNum, count);

    return count;
}

Decode Ways

Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12).

因为是求有多少种方法,并且这一位和前面的位数相关,所以是subproblem是有重叠的,当前的位置的合理解个数要考虑前面两个子问题的合理解数,可以用dp来做。

特殊的case: 101, 501, 110

  • 遇到有歧义的情况,当前的位置的合理解个数要考虑前面两个子问题的合理解数,dp[i] 要看 dp[i - 1] 和dp[i - 2]
  • 前一位不是0, 并且能和当前digit组成合理字母,dp[i] = dp[i - 1] + dp[i - 2]
  • 前一位组不了,不能形成新的线路,dp[i] = dp[i - 1]
  • 重点是0的处理
    • 开头遇到0,直接返回0
    • 任何位置连续两个零,无解,返回0
    • 前一位不能和当前0组成字母,无解返回0
    • 前一位能和当前0组成字母,dp[i] = dp[i - 2]
public int numberOfDecode(String s) {
    if(s == null || s.length() == 0) {
        return 0;
    }
    // cann't start with 0
    if(s.charAt(0) == '0') return 0;

    int n = s.length();
    int[] dp = new int[n + 1];
    dp[0] = 1;
    dp[1] = 1;

    for(int i = 1; i < n; i++) {
        int cur = s.charAt(i) - '0';
        int prev = s.charAt(i - 1) - '0';
        int num = prev * 10 + cur;

        if(cur == 0) {
            if(prev == 0) return 0; // case--00
            if(num <= 26) dp[i + 1] = dp[i - 1];
            else return 0;
        } else {
            if(prev != 0 && num <= 26) dp[i + 1] = dp[i] + dp[i - 1]; // 可以和前面组成新的数
            else dp[i + 1] = dp[i];
        }
    }

    return dp[n];
}

results matching ""

    No results matching ""