139.单词拆分
标签: dynamic-programming
, hash-table
难度: Medium
通过率: 47.58%
原题链接: https://leetcode.com/problems/word-break/description/
题目描述
给定一个字符串 和一个字符串列表 作为字典,如果 可以拆分成一个由字典中单词组成的空格分隔序列,返回 。注意:字典中的单词可以重复使用。
解题思路
这个问题可以用动态规划来解决。我们用 表示字符串 是否可以拆分成字典中的单词序列。为了实现动态规划,需要以下步骤:
- 初始化:定义一个大小为 的布尔型数组 ,其中 是字符串 的长度。将 设为 ,因为空字符串总是可以被认为是由空单词组成的。
- 遍历字符串 的每个位置 ,对于每个位置 ,在字典中检查每个单词 :
- 如果 可以用在 位置上的话(即 ),并且 为 ,那么 就为 。
- 最后的答案为 ,它表示整个字符串是否可以被拆分。
代码实现
- Python
- C++
- JavaScript
- Java
def wordBreak(s, wordDict):
# 创建一个集合来快速查找字典中的单词
word_set = set(wordDict)
# dp[i] 表示 s 的前 i 个字符是否可以由字典中的单词组成
dp = [False] * (len(s) + 1)
# 空字符串可以被认为是由字典中的单词组成
dp[0] = True
# 遍历字符串的每一位
for i in range(1, len(s) + 1):
# 检查每个可能的前缀
for j in range(i):
# 如果前缀能形成,并且切片在字典中,这条路径成立
if dp[j] and s[j:i] in word_set:
dp[i] = True
break
# 返回整体字符串是否能被切割、组成
return dp[len(s)]
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> word_set(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 = 0; j < i; ++j) {
if (dp[j] && word_set.find(s.substr(j, i - j)) != word_set.end()) {
dp[i] = true;
break;
}
}
}
return dp[s.size()];
}
};
function wordBreak(s, wordDict) {
const wordSet = new Set(wordDict);
const dp = Array(s.length + 1).fill(false);
dp[0] = true;
for (let i = 1; i <= s.length; i++) {
for (let j = 0; j < i; j++) {
if (dp[j] && wordSet.has(s.substring(j, i))) {
dp[i] = true;
break;
}
}
}
return dp[s.length];
}
import java.util.HashSet;
import java.util.List;
import java.util.Set;
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
Set<String> wordSet = new HashSet<>(wordDict);
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
for (int i = 1; i <= s.length(); i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && wordSet.contains(s.substring(j, i))) {
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
}
复杂度分析
时间复杂度:,其中 是字符串 的长度, 是字典中最长单词的长度。内循环最多需要扫描 个位置,且每个子字符串需要在集合中检查。
空间复杂度:,需要一个大小为 的数组 。