单词拆分
难度:
标签:
题目描述
给你一个字符串 s
和一个字符串列表 wordDict
作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s
则返回 true
。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
示例 1:
输入: s = "leetcode", wordDict = ["leet", "code"] 输出: true 解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。
示例 2:
输入: s = "applepenapple", wordDict = ["apple", "pen"] 输出: true 解释: 返回 true 因为"
applepenapple"
可以由"
apple" "pen" "apple" 拼接成
。 注意,你可以重复使用字典中的单词。
示例 3:
输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"] 输出: false
提示:
1 <= s.length <= 300
1 <= wordDict.length <= 1000
1 <= wordDict[i].length <= 20
s
和wordDict[i]
仅由小写英文字母组成wordDict
中的所有字符串 互不相同
代码结果
运行时间: 17 ms, 内存: 16.0 MB
/*
* 思路:
* 仍然使用动态规划的方法,但是这次我们使用 Java Stream 来实现。
* 定义一个布尔数组 dp,其中 dp[i] 表示字符串 s 的前 i 个字符是否可以由 wordDict 中的单词拼接成。
* 初始化 dp[0] = true,因为空字符串可以由空字符串组成。
* 使用 IntStream 来遍历字符串 s 的每一个位置 i,对于每一个位置 j,如果 dp[j] 为 true 并且 s.substring(j, i) 在 wordDict 中,那么 dp[i] = true。
* 最终 dp[s.length()] 就是答案。
*/
import java.util.*;
import java.util.stream.IntStream;
public 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;
IntStream.rangeClosed(1, s.length()).forEach(i -> {
IntStream.range(0, i).filter(j -> dp[j] && wordSet.contains(s.substring(j, i))).findFirst().ifPresent(j -> dp[i] = true);
});
return dp[s.length()];
}
}
解释
方法:
这个题解使用动态规划来解决单词拆分问题。它将问题转化为一个完全背包问题,其中背包的容量为字符串s的长度,物品为字典wordDict中的单词。dp[j]表示物品能否达成容量为j的背包。状态转移方程为:dp[j] = dp[j] or (dp[j - len(wordDict[i])] and s[j - len(wordDict[i]) : j] == wordDict[i]),即如果dp[j - len(wordDict[i])]为True,且s[j - len(wordDict[i]) : j]等于wordDict[i],则dp[j]为True。最后返回dp[len(s)]即可得到结果。
时间复杂度:
O(len(s) * len(wordDict))
空间复杂度:
O(len(s))
代码细节讲解
🦆
动态规划中的状态转移方程 `dp[j] = dp[j] or (dp[j - len(wordDict[i])] and s[j - len(wordDict[i]) : j] == wordDict[i])` 是如何确保不会重复使用同一位置的字符?
▷🦆
在动态规划的实现中,对于 `dp` 数组的初始化,为什么只有 `dp[0]` 被设置为 `True`,而其他位置初始化为 `False`?
▷🦆
题解中提到这是一个完全背包问题,如何解释字符串 `s` 和 `wordDict` 之间的关系与传统的完全背包问题中的物品和容量之间的关系?
▷🦆
为什么题解建议外层循环遍历背包容量,内层循环遍历物品,这种遍历顺序对问题解决有什么特别的帮助?
▷相关问题
单词拆分 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 <= 20
1 <= wordDict.length <= 1000
1 <= wordDict[i].length <= 10
s
和wordDict[i]
仅有小写英文字母组成wordDict
中所有字符串都 不同