139 单词拆分

本文最后更新于:2021年2月28日 晚上

给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

说明:

  • 拆分时可以重复使用字典中的单词。
  • 你可以假设字典中没有重复的单词。

示例 1:

1
2
3
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"

示例 2:

1
2
3
4
输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"
注意你可以重复使用字典中的单词。

示例 3:

1
2
输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

Solution

参考 @LeetCode官方代码随想录

  • 暴力回溯,超时
  • 时间复杂度 O(n^2^),每个单词都有两个状态,切割和不切割
  • 空间复杂度 O(n),算法递归调用系统栈
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
return go(s, wordSet, 0);
}

private:
bool go(const string& s, const unordered_set<string>& wordSet, int start) {
if (start >= s.size()) {
return true;
}
for (int i = start; i < s.size(); ++i) {
string word = s.substr(start, i - start + 1);
if (wordSet.find(word) != wordSet.end() && go(s, wordSet, i + 1)) {
return true;
}
}
return false;
}
};
  • 记忆化搜索
  • 技巧,涉及bool状态时,初始化 -1,false 为 0,true 为 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
memo = vector<int>(s.size(), -1);
return go(s, wordSet, 0);
}

private:
vector<int> memo;

bool go(const string& s, const unordered_set<string>& wordSet, int start) {
if (start >= s.size()) {
return true;
}
if (memo[start] != -1) return memo[start];
for (int i = start; i < s.size(); ++i) {
string word = s.substr(start, i - start + 1);
if (wordSet.find(word) != wordSet.end() && go(s, wordSet, i + 1)) {
memo[start] = 1;
return true;
}
}
memo[start] = 0;
return false;
}
};
  • 动态规划,转换成 完全背包问题

  • 单词就是物品,字符串s就是背包,单词能否组成字符串s,就是问物品能不能把背包装满。

  • 递推公式是 if([j, i] 这个区间的子串出现在字典里 && dp[j]是true) { dp[i] = true }

  • 考虑 遍历顺序

  • 时间复杂度:O(n^3),因为substr返回子串的副本是O(n)的复杂度(这里的n是substring的长度)

  • 空间复杂度:O(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> wordSet;
for(auto word : wordDict)
wordSet.insert(word);

vector<bool> dp(s.size()+1);
dp[0] = true;
for(int i=1; i<=s.size(); ++i){
for(int j=0; j<i; ++j){
if(dp[j] && wordSet.find(s.substr(j,i-j))!=wordSet.end()){
dp[i] = true;
break;
}
}
}
return dp[s.size()];
}
};

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!