持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第30天,点击查看活动详情
单词拆分
给定一个字符串 s 和一个字符串字典 wordDict ,在字符串 s 中增加空格来构建一个句子,使得句子中所有的单词都在词典中。以任意顺序 返回所有这些可能的句子。
注意: 词典中的同一个单词可能在分段中被重复使用多次。
1 <= s.length <= 201 <= wordDict.length <= 10001 <= wordDict[i].length <= 10s和wordDict[i]仅有小写英文字母组成wordDict中所有字符串都 不同
解法
这道题有一道简单版本的,就是不统计所有可能的句子,而是判断是否可以分割
题目描述为:
给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。
我们可以先从这一道题入手本问题。
对于这一道题的简化版本的而言:
可以观察得到一个很典型的状态转移
意思就是:
所以,这道题用动态规划的思想就很容易的解出来了。
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
int n = s.size();
vector<int> dp(n + 1, 0);
dp[0] = 1;
unordered_set<string> st;
for (auto v : wordDict) st.insert(v);
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= i && !dp[i]; j ++) {
string tm = s.substr(j-1, i-j+1);
dp[i] |= dp[j-1] & st.count(tm);
}
}
return dp[n];
}
};
现在,回到本题
现在要求算出具体方案,如何进行求解呢?
其实道理是相通的,核心在于计算其分割点。
现在,我们把眼光逆向看。
现在,只需要逆序的做这一件事情就好了,对于这个而已,用记忆化搜索会比较好写一点,所以,我们可以使用记忆化搜索来写,当然,写为dp从前向后推也是没有问题的,只是枚举顺序发生了改变。
class Solution {
public:
vector<string> wordBreak(string s, vector<string>& wordDict) {
int n = s.size();
unordered_set st(wordDict.begin(), wordDict.end());
vector<vector<string>> dp(n + 1, vector<string>());
dp[n].push_back("");
function<void(int)> dfs = [&](int idx) -> void {
if (dp[idx].size()) return;
for (int i = idx + 1; i <= n; i ++) {
auto tm = s.substr(idx, i - idx);
if (!st.count(tm)) continue;
dfs(i);
for (auto v : dp[i]) {
dp[idx].push_back(
v.size() == 0 ? tm : tm + " " + v
);
}
}
};
dfs(0);
return dp[0];
}
};