LeetCode 单词拆分II(回溯法+剪枝)

给定一个非空字符串 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"]
输出:
[]

请先翻阅 LeetCode 单词拆分
这道题不但要判断串是否可拆,还要把所有的情况列出来,对于这种题一般都是回溯法。

class Solution {
public:
	set<int> wordLengthSet;//用于储存wordDict中单词的长度
	vector<string> result;
	vector<string> wordBreak(string &s, vector<string>& wordDict) {
		if (s == "" || wordDict.size() == 0) {
			return {};
		}
		//首先扫描一遍,获取wordDict长度的种类
		for (auto word : wordDict) {
			wordLengthSet.insert(word.size());
		}
		//对初始长度进行穷举
		for (auto it = wordLengthSet.rbegin(); it != wordLengthSet.rend(); ++it) {
			string subS = s.substr(0, *it);//以nowIndex为起始,获取*it长的子串
			if (find(wordDict.begin(), wordDict.end(), subS) != wordDict.end()) {
				dfs(wordDict, s, *it, subS);
			}
		}
		return result;
	}
	void dfs(vector<string>& wordDict, string &str, int nowIndex, string tempRes) {
		int strSize = str.size();
		if (nowIndex >= strSize) {//此次拆分成功
			result.push_back(tempRes);//保留结果
			return;
		}
		//对长度进行穷举
		for (auto it = wordLengthSet.rbegin(); it != wordLengthSet.rend(); ++it) {
			string subS = str.substr(nowIndex, *it);//以nowIndex为起始,获取*it长的子串
			if (find(wordDict.begin(), wordDict.end(), subS) != wordDict.end() && nowIndex + *it <= strSize) {
				dfs(wordDict, str, nowIndex + *it, tempRes + " " + subS);
			}
		}
	}
};

但是超时了。。。
在这里插入图片描述
优化思路:利用上一题的动态规划标记法,标记[i, s.size() - 1]是否可拆分,如果不可拆分就跳过。(剪枝)

class Solution {
public:
	set<int> wordLengthSet;//用于储存wordDict中单词的长度
	vector<string> wordBreak(string s, vector<string>& wordDict) {
		vector<string> result;
		string tempRes;
		vector<bool> possible(s.size() + 1, true);//possible[i]用于标记[i, s.size() - 1]是否可拆,初始化都可拆
		//首先扫描一遍,获取wordDict长度的种类
		for (auto word : wordDict) {
			wordLengthSet.insert(word.size());
		}
		wordBreakDFS(s, wordDict, 0, possible, tempRes, result);//开始搜索
		return result;
	}
	void wordBreakDFS(string &s, vector<string> &wordDict, int start, vector<bool> &possible, string &tempRes, vector<string> &result) {
		int strSize = s.size();
		if (start == s.size()) {//此次搜索成功
			result.push_back(tempRes.substr(0, tempRes.size() - 1));//需要去掉末尾的空格
			return;
		}
		//对长度进行穷举
		for (auto it = wordLengthSet.begin(); it != wordLengthSet.end() && start + *it <= strSize; ++it) {
			string word = s.substr(start, *it);
            //possible[start + *it + 1]为增加的判断条件,判断[0, start + *it]是否可拆
			if (find(wordDict.begin(), wordDict.end(), word) != wordDict.end() && possible[start + *it + 1]) {
				tempRes.append(word).append(" ");//添加word、空格
				int oldSize = result.size();
				wordBreakDFS(s, wordDict, start + *it, possible, tempRes, result);
				if (result.size() == oldSize){//如果经过上面的dfs,结果数还未增加,说明后面这段不可拆
                    possible[start + *it + 1] = false;
                }
				tempRes.resize(tempRes.size() - word.size() - 1);//使用完后记得恢复tempRes字符串
			}
		}
	}
};

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_41855420/article/details/87896084