LeetCode-动态规划单词拆分
Posted Flix
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode-动态规划单词拆分相关的知识,希望对你有一定的参考价值。
题目描述
给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
说明:
- 拆分时可以重复使用字典中的单词。
- 你可以假设字典中没有重复的单词。
示例:
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。
输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
? 注意你可以重复使用字典中的单词。
输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false
题目链接: https://leetcode-cn.com/problems/word-break/
思路1
使用动态规划来做。
- 状态定义:使用 dp[i] 表示从字符串头开始长度为 i 的字符串是否能被拆分为字典中的单词,令边界条件dp[0]=true。例如,对于例子
s = "leetcode", wordDict = ["leet", "code"]
来说,dp[4]=true,因为 leet 在 wordDict 中。 - 状态转移公式:dp[i] = dp[j] && (s[j,...,i] in wordDict),j 在 [0, i-1] 之间。还拿上一个例子来说,我们要判断 dp[4] 的值,我们可以看:
- dp[3] && ("t" in wordDict);
- dp[2] && ("et" in wordDict);
- dp[1] && ("eet" in wordDict);
- dp[0] && ("leet" in wordDict);
只要这 4 种情况有一种为 true,则说明 "leet" 能被拆分,所以 dp[4]=true。
代码如下:
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> lookup(wordDict.begin(), wordDict.end());
vector<bool> dp(s.size()+1, false);
dp[0] = true;
for(int i=1; i<=s.size(); i++){
for(int j=i-1; j>=0; j--){
dp[i] = dp[j] && (lookup.count(s.substr(j, i-j))!=0);
if(dp[i]) break;
}
}
return dp[s.size()];
}
};
- 时间复杂度:O(n^2)
- 空间复杂度:O(n)
思路2
使用回溯来做。代码如下:
class Solution {
bool ans = false;
public:
bool wordBreak(string s, vector<string>& wordDict) {
int start = 0;
unordered_set<string> lookup(wordDict.begin(), wordDict.end());
dfs(s, start, lookup);
return ans;
}
void dfs(string s, int start, unordered_set<string> lookup){
if(start>=s.size()){
ans = true;
return;
}
if(ans) return;
for(int i=start+1; i<=s.size(); i++){ // 注意是 i<=s.size(),不是 i<s.size()
if(lookup.count(s.substr(start, i-start))>0){
dfs(s, i, lookup);
}
}
}
};
思路3
备忘录算法。我们使用一个备忘录unordered_map<string, bool> memo
对思路 2 进行改进,也就是记录下那些不成功的子串分割,如果遇到之前出现的子串并有不成功的记录就不用往下递归了。代码如下:
class Solution {
bool ans = false;
public:
bool wordBreak(string s, vector<string>& wordDict) {
int start = 0;
unordered_set<string> lookup(wordDict.begin(), wordDict.end());
unordered_map<string, bool> memo; // 备忘录
dfs(s, start, lookup, memo);
return ans;
}
void dfs(string s, int start, unordered_set<string> lookup, unordered_map<string, bool>& memo){
if(start>=s.size()){
ans = true;
return;
}
if(ans) return;
for(int i=start+1; i<=s.size(); i++){ // 注意是 i<=s.size(),不是 i<s.size()
string sub = s.substr(start, i-start);
if(lookup.count(sub)>0){
if(memo.count(sub)==0 || memo[sub]!=false) dfs(s, i, lookup, memo);
}
memo[sub] = false; // 这样分割不行,记录下来不成功的子串
}
}
};
以上是关于LeetCode-动态规划单词拆分的主要内容,如果未能解决你的问题,请参考以下文章