LeetCode算法練習——動態規劃提高(四)

LeetCode139. 單詞拆分

給定一個非空字符串 s 和一個包含非空單詞列表的字典 wordDict,判定 s 是否可以被空格拆分爲一個或多個在字典中出現的單詞。

示例 1:

輸入: s = "leetcode", wordDict = ["leet", "code"]
輸出: true
解釋: 返回 true 因爲 "leetcode" 可以被拆分成 "leet code"。

示例 2:

輸入: s = "applepenapple", wordDict = ["apple", "pen"]
輸出: true
解釋: 返回 true 因爲 "applepenapple" 可以被拆分成 "apple pen apple"。
     注意你可以重複使用字典中的單詞。

示例 3:

輸入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
輸出: false
  • 1. 定義:dp[i]表示s的前i個字符能否拆分成一個或多個wordDict字典中的單詞 所以需要定義dp的長度爲s.size() + 1;
  • 2. dp[0] = true 表示空字串是可以拆分成字典中的單詞 即:前0個字符是可以拆分成字典中的單詞;
  • 3. 狀態轉移方程: dp[i] = dp[j] && s[j, i - 1] dp[j]表示s[0, j - 1]和s[j, i - 1]是否同時在字典中;
  • 4. s = "leetcode", wordDict = ["leet", "code"],dp[8] = dp[4] + check("code") (1)dp[4] = true 表示s[0, 3] = "leet"是字典中的單詞 (2) check("code") 即s[4,7] 表示檢查"code"是不是字典中的單詞。
class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> set;
        for(string str : wordDict) set.insert(str);
        int length = s.size();
        vector<bool> dp(length + 1, false);
        dp[0] = true;
        for(int i = 1; i < length + 1; i++){
            for(int j = 0; j < i; j++){
                if(dp[j] && set.find(s.substr(j, i - j)) != set.end()){//集合中找尋子串
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[length];
    }
};

LeetCode152. 乘積最大子數組

給你一個整數數組 nums ,請你找出數組中乘積最大的連續子數組(該子數組中至少包含一個數字),並返回該子數組所對應的乘積。

示例 1:

輸入: [2,3,-2,4]
輸出: 6
解釋: 子數組 [2,3] 有最大乘積 6。

示例 2:

輸入: [-2,0,-1]
輸出: 0
解釋: 結果不能爲 2, 因爲 [-2,-1] 不是子數組。

此題思路是用貪心算法,實際可以歸類於是一種動態規劃問題,由於存在負數,我們需要記錄當前最大值和當前最小值。每次遍歷用先前的最值乘以當前值,更新最值和最大乘積。

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int len = nums.size(), res = nums[0];
        int prevMin = nums[0], prevMax = nums[0];
        int nagtive = 0, postive = 0;
        for (int i = 1; i < len; i++) {
            nagtive = prevMin * nums[i];                      //最小負值*當前值
            postive = prevMax * nums[i];                      //最大正值*當前值
            prevMin = min(min(nagtive, postive), nums[i]);    //當前最小值
            prevMax = max(max(nagtive, postive), nums[i]);    //當前最大值
            res = max(prevMax, res);
        }
        return res;
    }
};

LeetCode377. 組合總和 Ⅳ

給定一個由正整數組成且不存在重複數字的數組,找出和爲給定目標正整數的組合的個數。

示例:

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

所有可能的組合爲:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)

請注意,順序不同的序列被視作不同的組合。

因此輸出爲 7。

 此題有點類似與LeetCode518零錢兌換2的思路,不同點在於,元素出現順序不同也是一種組合,思路如下:

狀態

對於“狀態”,我們首先思考能不能就用問題當中問的方式定義狀態,上面遞歸樹都畫出來了。當然就用問題問的方式。dp[i] :對於給定的由正整數組成且不存在重複數字的數組,和爲 i 的組合的個數。思考輸出什麼?因爲狀態就是問題當中問的方式而定義的,因此輸出就是最後一個狀態 dp[n]。

狀態轉移方程

由上面的樹形圖,可以很容易地寫出狀態轉移方程:

dp[i] = sum{dp[i - num] for num in nums and if i >= num}

注意:在 0 這一點,我們定義 dp[0] = 1 的,它表示如果 nums 裏有一個數恰好等於 target,它單獨成爲 1 種可能。

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<unsigned long long> dp(target + 1, 0);
        dp[0] = 1;
        for(int i = 1; i <= target; i++){
            for(int j = 0; j < nums.size(); j++){
                if(i - nums[j] >= 0)
                    dp[i] += dp[i - nums[j]];
                else continue;
            }
        }
        return dp[target];
    }
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章