开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第40天,点击查看活动详情
LeetCode 140. Word Break II
给定一个字符串 s 和一个字符串字典 wordDict ,在字符串 s 中增加空格来构建一个句子,使得句子中所有的单词都在词典中。以任意顺序 返回所有这些可能的句子。
注意: 词典中的同一个单词可能在分段中被重复使用多次。
示例 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"]
输出: []
提示:
1 <= s.length <= 201 <= wordDict.length <= 10001 <= wordDict[i].length <= 10s和wordDict[i]仅有小写英文字母组成wordDict中所有字符串都 不同
算法
(动态规划 + 递归枚举)
按照 Word Break 的动态规划思路,求出 f 数组,f(i) 表示 [0, i] 是否可以被完整分割。
根据 f 数组,从位置 n 开始进行递归枚举所有可能分割的方式。枚举时,只需要判断 f(i) 是否可能从 f(j) 转移过来的,若可能,则递归到位置 j。
时间复杂度
动态规划的时间复杂度为 O(n3)。
递归枚举的时间复杂度与方案数相等,为指数级。
ac 代码
class Solution {
public:
void dfs(int i, const vector<bool>& f, const string &s,
const unordered_set<string>& wordSet,
vector<string>& cur_string, vector<string>& ans) {
if (i == 0) {
string tmp = "";
for (int k = cur_string.size() - 1; k >= 0; k--) {
tmp += cur_string[k];
if (k > 0)
tmp += " ";
}
ans.push_back(tmp);
return;
}
for (int j = 0; j < i; j++) {
string cur = s.substr(j, i - j);
if (f[j] && wordSet.find(cur) != wordSet.end()) {
cur_string.push_back(cur);
dfs(j, f, s, wordSet, cur_string, ans);
cur_string.pop_back();
}
}
}
vector<string> wordBreak(string s, vector<string>& wordDict) {
int n = s.length();
unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
vector<bool> f(n + 1, false); // 为了方便,f 数组的下标从 1 开始。
f[0] = true;
for (int i = 1; i <= n; i++)
for (int j = 0; j < i; j++) {
string cur = s.substr(j, i - j);
if (wordSet.find(cur) != wordSet.end())
f[i] = f[i] | f[j];
}
vector<string> ans;
vector<string> cur_string;
dfs(n, f, s, wordSet, cur_string, ans);
return ans;
}
};