DP (4) -- Largest Divisible Subset, Coin Change, Unique Substrings in Wraparound String

Largest Divisible Subset

Given a set of distinct positive integers, find the largest subset such that every pair (Si, Sj) of elements in this subset satisfies: Si % Sj = 0 or Sj% Si = 0.

If there are multiple solutions, return any subset is fine.

Example 1:

nums: [1,2,3]

Result: [1,2] (of course, [1,3] will also be ok)
1. 在有序的情况下,如果 Si % Sj = 0 且 i > j,Si可以被以Sj为最大值的subset的所有数整除。

2. 以数组dp存储包含该数的最大subset的长度,则dp[i] = max(dp[i], dp[j] + 1), -1 < j < i.

3. 可以用二维数组存储包含每个nums[i]的最大subset的元素。

4. 相比于步骤3种的解法,可以用一维数组parent存储当前最大subset的上一元素。比如[1, 2, 3, 4, 8]。如果按照步骤3来存储,4和8所存储的元素其实只多了一个8。因此我们使用这种链式的方式存储。8 -> 4 -> 2 -> 1

5. 记录最大subset


    vector<int> largestDivisibleSubset(vector<int>& nums) {
        if(nums.empty()) return vector<int>();
        sort(nums.begin(), nums.end());
        vector<int> dp(nums.size(), 0);
        vector<int> parent(nums.size(),-1);
        int totalMaxSize = 0, totalMaxIndex = -1;
        for(int i = 0; i < nums.size(); i++){
            int maxSize = 0, maxIndex = 0;
            for(int j = i-1; j > -1; j--){
                if(!(nums[i] % nums[j]) && dp[j] + 1 > dp[i]){
                    dp[i] = dp[j] + 1;
                    parent[i] = j;
                }
            }
            if(dp[i] > totalMaxSize){
                totalMaxSize = dp[i];
                totalMaxIndex = i;
            }
        }
        vector<int> rst;
        int i = totalMaxIndex;
        while(i > -1){
            rst.push_back(nums[i]);
            i = parent[i];
        }
        return rst;
    }


Coin Change

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

Example 1:
coins = [1, 2, 5], amount = 11
return 3 (11 = 5 + 5 + 1)

1. 这种求最大最小的问题,一般不使用穷举。Can I Win要求所有组合都不可能,但在这道题中我们只关心最优。

2. 背包问题的变种,相当于背包价值是固定的,物品的数量是不固定。从最后一步往前想,最后一步要挑一枚硬币coins[j],则我们需要知道amount - coins[j]的最优解;依次类推有: dp[i] = min(dp[i], dp[i - coins[j]+1]

    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount+1, amount+1);
        dp[0] = 0;
        for(int i = 1; i <= amount; i++){
            for(int j = 0; j < coins.size(); j++){
                if(coins[j] <= i) dp[i] = min(dp[i], dp[i-coins[j]] + 1);
            }
        }
        return dp[amount] > amount ? -1 : dp[amount];
    }


Unique Substrings in Wraparound String

Consider the string s to be the infinite wraparound string of "abcdefghijklmnopqrstuvwxyz", so s will look like this: "...zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd....".

Now we have another string p. Your job is to find out how many unique non-empty substrings of p are present in s. In particular, your input is the string p and you need to output the number of different non-empty substrings of p in the string s.

Note: p consists of only lowercase English letters and the size of p might be over 10000.

Input: "zab"
Output: 6
Explanation: There are six substrings "z", "a", "b", "za", "ab", "zab" of string "zab" in the string s.

1. 记录以每个字母结尾的最大子串的长度。最大子串的长度就是以该字母结尾的子串的数量。

2. 比如以'd' 结尾的长度为4,则包含的子串为 ‘d’,'cd', 'bcd', 'abcd'。这样记录的好处是不用考虑的重合区间的问题,因为小范围是大范围的子集。

3. 开始的想法是只记录结尾处的字母子串长度,然后以长度为单位去计算子串个数,如长度为2的子串有多少。但是这样就需要在后续处理重合区间的问题,很麻烦。

4. 记录以每个字母开头的最大子串长度也是可以的,但是每次需要长度增加都需要更新前面的数值,并不是o(n)的解法


    int findSubstringInWraproundString(string p) {
        char last = p[0];
        int len = 1;
        vector<int> rcd(26, 0);
        for(int i = 0; i < p.size(); i++){
            if(p[i] - last == 1 || (last == 'z' && p[i] == 'a')) len++;
            else len = 1;
            rcd[p[i]-'a'] = max(len, rcd[p[i]-'a']);
            last = p[i];
        }
        int strNum = 0;
        for(int num: rcd) strNum += num;
        return strNum;
    }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值