【每日一題】LeetCode. 139. 單詞拆分

每日一題,防止癡呆 = =

一、題目大意

給定一個非空字符串 s 和一個包含非空單詞列表的字典 wordDict,判定 s 是否可以被空格拆分爲一個或多個在字典中出現的單詞。
在這裏插入圖片描述
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/word-break

二、題目思路以及AC代碼

思路

這道題的思路感覺挺常規的,是遞進式的。

一階段:遞歸

首先容易想到的是遞歸,我們可以直接遞歸來做,思路很清晰。就是首先用unordered_map存儲字典,然後建立遞歸函數can_break(s),其中,每個遞歸函數中,找到第一個和字典中元素匹配的元素,然後將去掉這個元素後的子串再繼續遞歸,最後就可以遍歷所有的情況,從而求得結果。

當然,這個方法在這裏會超時,畢竟有太多不必要的計算了。

二階段:動態規劃

自然,我們遞歸超時了,就可以考慮用動態規劃來進行求解。我們設 dp[i] 表示字符串的前 i 個字符,是否可以拆解。那麼我們進行遞推的時候,如果要求解 dp[i],那麼我們只需要遍歷 dp[0] ~ dp[i - 1],如果其中 dp[j] 爲 true,也就是說前 j 個字符已經是可拆的了,那麼我們只要保證 j + 1 ~ i 這個字符串在字典中,那麼前 i 個字符就都是可拆的了,這樣就有了遞推關係。時間複雜度是O(N2),相比於前一種已經減少很多了。

三階段:優化的動態規劃

到了上面的方法就是結束了嗎?當然不是,我們還能進行一些常數上的加速。我們上面需要在每次求解 dp[i] 的時候,遍歷所有的 0 ~ i - 1,然而我們可以主動一點,我們在遍歷到 i 的時候,下一步不是去遍歷 0 ~ i - 1,而是去遍歷 wordList,去求 dp[i + word.size() - 1],這樣就又可以省去一些重複遍歷 dp中元素的時間,比二階段的方法更進一步。

AC代碼

遞歸解法

class Solution {
private:
    map<string, bool> wordMap;
public:
    bool can_break(string s) {
        if (s.empty()) return true;

        int s_len = s.length();
        string temp = "";
        for (int i=0;i<s_len;i++) {
            temp += s[i];
            if (wordMap[temp] && can_break(s.substr(i + 1))) return true;
        }

        return false;
    }

    bool wordBreak(string s, vector<string>& wordDict) {
        for (string word: wordDict) {
            wordMap[word] = true;
        }

        return can_break(s);
    }
};

common 動態規劃解法

class Solution {
private:
    unordered_map<string, bool> wordMap;
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        for (string word: wordDict) {
            wordMap[word] = true;
        }

        int s_len = s.length();
        bool dp[s_len + 1];
        for (int i=0;i<=s_len;i++) dp[i] = false;
        dp[0] = true;

        for (int i=1;i<=s_len;i++) {
            for (int j=0;j<i;j++) {
                if (dp[j] && wordMap[s.substr(j, i - j)]) {
                    dp[i] = true;
                    break;
                }
            }
        }

        return dp[s_len];
    }
};

優化動態規劃解法

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        int s_len = s.length();
        bool dp[s_len + 1];
        for (int i=0;i<=s_len;i++) dp[i] = false;
        dp[0] = true;

        for (int i=1;i<=s_len;i++) {
            for (string word: wordDict) {
                if (dp[i-1] && i + word.length() - 1 <= s_len && word == s.substr(i - 1, word.length())) {
                    dp[i + word.length() - 1] = true;
                }
            }
        }

        return dp[s_len];
    }
};

如果有問題,歡迎大家指正!!!

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