LeetCode 單詞拆分

hestyle發表於2019-02-23

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

方法一:回溯法(深度優先搜尋法),即每次從當前剩餘待匹配的串中擷取一段,然後判斷wordDict是否存在,如果存在則繼續下一步的搜尋,否則更改當前的擷取長度,繼續匹配。
但是超時了。。。

class Solution {
public:
	set<int> wordLengthSet;//用於儲存wordDict中單詞的長度
	map<string, int> wordDictMap;//將string與是否出現關聯
	bool wordBreak(string &s, vector<string>& wordDict) {
		//首先掃描一遍,獲取wordDict長度的種類
		for (auto word : wordDict) {
			wordLengthSet.insert(word.size());
			wordDictMap[word] += 1;
		}
		return dfs(s, 0);
	}
	bool dfs(string &str, int nowIndex) {
		int strSize = str.size();
		if (nowIndex >= strSize) {
			return true;
		}
		//對長度進行窮舉
		for (auto it = wordLengthSet.rbegin(); it != wordLengthSet.rend(); ++it) {
			string subS = str.substr(nowIndex, *it);//以nowIndex為起始,獲取*it長的子串
			if (wordDictMap[subS] > 0 && dfs(str, nowIndex + *it)) {
				return true;
			}
		}
		return false;
	}
};

在這裡插入圖片描述
方法二:將回溯法修改為廣度優先搜尋法。但是還是超時了。。。

class Solution {
public:
	bool wordBreak(string &s, vector<string>& wordDict) {
		if (s == "") {
			return true;
		}
		set<int> wordLengthSet;//用於儲存wordDict中單詞的長度
		int strSize = s.size();
		//首先掃描一遍,獲取wordDict長度的種類
		for (auto word : wordDict) {
			wordLengthSet.insert(word.size());
		}
		queue<int> myQueue;
		myQueue.push(0);
		int tempQueueSize, tempBegin;
		while (!myQueue.empty()) {
			tempQueueSize = myQueue.size();
			for (int i = 0; i < tempQueueSize; ++i) {
				tempBegin = myQueue.front();
				myQueue.pop();
				for (auto it = wordLengthSet.rbegin(); it != wordLengthSet.rend(); ++it) {
					string subS = s.substr(tempBegin, *it);
					if (find(wordDict.begin(), wordDict.end(), subS) != wordDict.end()) {
						if (tempBegin + *it >= strSize) {
							return true;
						}
						myQueue.push(tempBegin + *it);
					}
				}
			}
		}
		return false;
	}

};

方法三:動態規劃法。

class Solution {
public:
	bool wordBreak(string &s, vector<string>& wordDict) {
		if (s == "") {
			return true;
		}
		int strSize = s.size();
		vector<bool> dp(strSize, false);//dp[i] == true表示[0, i]可以有wordDict拼湊出
		for (int i = 0; i < strSize; ++i) {
			if (i == 0 || dp[i - 1]) {//只有當i為0(s串起始)或者 [0, i - 1]能拼湊出才有匹配的意義
				for (int j = i; j < strSize; ++j) {//對長度進行窮舉
					string subS = s.substr(i, j - i + 1);//以i為起始j - i + 1為長度,求子串
					if (find(wordDict.begin(), wordDict.end(), subS) != wordDict.end()) {
						dp[j] = true;
					}
				}
			}
			
		}
		return dp[strSize - 1];
	}

};

在這裡插入圖片描述
方法三優化:第二層for迴圈窮舉長度時,比較笨,每一個長度都進行窮舉,但其實wordDict最長的長度都有一個限度,最大值,所以長度窮舉需要控制。

class Solution {
public:
	bool wordBreak(string &s, vector<string>& wordDict) {
		if (s == "") {
			return true;
		}
		int strSize = s.size();
		vector<bool> dp(strSize, false);//dp[i] == true表示[0, i]可以有wordDict拼湊出
		//獲取最長字串長度
		int maxWordLength = 0;
		for (int i = 0; i < wordDict.size(); ++i) {
			maxWordLength = max(maxWordLength, (int)wordDict[i].size());
		}
		for (int i = 0; i < strSize; ++i) {
			if (i == 0 || dp[i - 1]) {//只有當i為0(s串起始)或者 [0, i - 1]能拼湊出才有匹配的意義
				for (int len = maxWordLength; len > 0; --len) {//對長度進行窮舉
					string subS = s.substr(i, len);//以i為起始len為長度,求子串
					if (find(wordDict.begin(), wordDict.end(), subS) != wordDict.end()) {
						dp[i + len - 1] = true;
					}
				}
			}

		}
		return dp[strSize - 1];
	}
};

在這裡插入圖片描述

相關文章