動態規劃解題算法3

1、單詞拆分

給定一個非空字符串 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

方法一:通過兩個下標對字符串進行控制,如果當前字符串s[j:i]存在於 wordDict 中,把右下標加到 temp 中,那麼下次就可以從 i 下標爲起點(左下標)來判斷字符串…這樣,temp 最後一個值一定就是字符串長度,否則就無法分割

    def wordBreak(s, wordDict):
        n = len(s)
        temp = [0]
        for i in range(n+1):
            for j in temp:
                if s[j:i] in wordDict:
                    temp.append(i)
                    break
        return temp[-1] == n

方法二:利用 C++ 容器中 unordered_set 對字符串進行分割和查找

    bool wordBreak(string s, vector<string>& wordDict) {
        vector<bool>dp(s.size()+1,false);
        unordered_set<string>m(wordDict.begin(),wordDict.end());
        dp[0] = true;
        for(int i = 0;i < s.size()+1;++i){
            for(int j = 0;j<i;++j){
                if(dp[j] && m.find(s.substr(j,i-j))!=m.end()){
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.size()];
    }

進階:給定一個非空字符串 s 和一個包含非空單詞列表的字典 wordDict,在字符串中增加空格來構建一個句子,使得句子中所有的單詞都在詞典中。返回所有這些可能的句子

說明:
分隔時可以重複使用字典中的單詞。
你可以假設字典中沒有重複的單詞。

示例1
輸入:
s = “catsanddog”
wordDict = [“cat”, “cats”, “and”, “sand”, “dog”]
輸出:
[
“cats and dog”,
“cat sand dog”
]

示例2
輸入:
s = “pineapplepenapple”
wordDict = [“apple”, “pen”, “applepen”, “pine”, “pineapple”]
輸出:
[
“pine apple pen apple”,
“pineapple pen apple”,
“pine applepen apple”
]
解釋: 注意你可以重複使用字典中的單詞。

示例3
輸入:
s = “catsandog”
wordDict = [“cats”, “dog”, “sand”, “and”, “cat”]
輸出:
[]

先判斷是否可以分割,然後進行回溯,有一定的難度

class Solution {
public:
	// 判斷是否可以分割
    bool wordBreak2(string s, vector<string>& wordDict) {
        vector<bool>dp(s.size()+1,false);
        unordered_set<string>m(wordDict.begin(),wordDict.end());
        dp[0] = true;
        for(int i = 0;i < s.size()+1;++i){
            for(int j = 0;j<i;++j){
                if(dp[j] && m.find(s.substr(j,i-j))!=m.end()){
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.size()];
    }
    // 進行回溯
    void helper(string s, int l, int r){
        if(l>r){
            string str;
            for(int i=0; i<path.size()-1; i++)
                str = str + path[i] + " ";
            str += path.back();
            ans.push_back(str);
            return ;
        }
        // i是截取字符串的長度
        for(int i=1; i<=r-l+1; i++){
            string tmp = s.substr(l, i);
            if(sstr.find(tmp) != sstr.end()){
                path.push_back(tmp);
                helper(s, l+i, r);
                path.pop_back();
            }
        }  
    }
    vector<string> wordBreak(string s, vector<string>& wordDict) {
        if(!wordBreak2(s, wordDict)){
            return ans;
        }
        sstr = unordered_set<string>(wordDict.begin(), wordDict.end());
        helper(s, 0, s.size()-1);
        return ans;
    }
private:
    unordered_set<string>sstr;
    vector<string>ans;
    vector<string>path;
};

2、最大子序列之和

給定一個整數數組 nums ,找到一個具有最大和的連續子數組(子數組最少包含一個元素),返回其最大和

示例
輸入: [-2,1,-3,4,-1,2,1,-5,4],
輸出: 6
解釋: 連續子數組 [4,-1,2,1] 的和最大,爲 6

動態規劃解題:temp [i]表示到第i爲時由第i個數爲序列最後一個數時的最大和,若temp [i-1]>0,果斷連起來,加 temp[i];反之,以當前數位序列的第一個數;每一位時都記錄到目前爲止最大的子序列和。

C++ 參考代碼

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        if(nums.empty()) return 0;
        int n = nums.size();
        vector<int>dp(n,0);
        dp[0] = nums[0];
        int ans = dp[0];
        for(int i = 1;i<n;++i){
            // 如果前 i 和數字之和大於0,則加當前元素
            if(dp[i-1]>0){
                dp[i] = dp[i-1]+nums[i];
            }else{
                // 如果小於0則把當前元素賦值給dp
                dp[i] = nums[i];
            }
            ans = max(ans,dp[i]);
        }
        return ans;
    }
};

方法二:可以不保存前 i 個元素之和,直接進行當前元素與最大值的判斷,並且時刻更新最大值,最後返回,空間複雜度爲O(1)

C++ 參考代碼

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        if(nums.empty()) return 0;
        int n = nums.size();
        int temp = nums[0];
        int ans = temp;
        for(int i = 1;i<n;++i){
            if(temp > 0){
                temp += nums[i];
            }else{
                temp = nums[i];
            }
            ans = max(ans,temp);
        }
        return ans;
    }
};

3、最長有效括號

給定一個只包含 ‘(’ 和 ‘)’ 的字符串,找出最長的包含有效括號的子串的長度。

示例1
輸入: “(()”
輸出: 2
解釋: 最長有效括號子串爲 “()”

示例2
輸入: “)()())”
輸出: 4
解釋: 最長有效括號子串爲 “()()”

思路:dp[i]表示以第i個字符結尾的有效括號的最長長度

對於( )(( ))
i=4: dp = [0,2,0,0,2,0]
i=5: dp = [0,2,0,0,2,6]
dp[5] = 2 + dp[5-dp[5-1]-2]

解釋:dp[i] 和dp[i-1]有關,還和dp[i-dp[i-1]-2]有關,沒有第5位的‘)’時, 第2位的‘(’把0-4的字串分成了兩端有效括號,但有了第5位的‘)’時,激活了這兩段之間的連接,使之形成一整段有效括號

當s[i] == ‘(’:dp[i] = 0
當s[i] == ‘)’,並能找到前半個‘(’:
狀態轉移方程:dp[i] = dp[i-dp[i-1]-2] + 2+ dp[i-1]

def longestValidParentheses(self, s):
    max_len = 0
    len_s = len(s)
    dp = [0]*len_s
    for i in range(1,len_s):
        if s[i] ==')':
            if i-dp[i-1]-1>=0 and s[i-dp[i-1]-1]=='(':                  
                dp[i] = dp[i-dp[i-1]-2] + 2+ dp[i-1]
                max_len = max(max_len, dp[i])            
    return max_len
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章