LeetCode139. 单词拆分
题目链接:139. 单词拆分
题目描述:
给你一个字符串 s
和一个字符串列表 wordDict
作为字典。请你判断是否可以利用字典中出现的单词拼接出 s
。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
示例 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
中的所有字符串 互不相同
算法分析:
这时一个背包问题,单词就是物品,字符串就是背包。
字符串可否有字典当中的单词组成,意思就是背包可否有物品装满。
而且题目描述字典当中的单词可以重复使用,所以这是完全背包问题。
我们用动规五部曲来。
定义dp数组及下标含义:
dp[j]=true,我们定义为下标为j之前的字符串可以拆分成字典当中一个会多个单词组成。
递推公式:
如果[i,j)这个区间的字符串出现在字典里(注意截取字符串的时候是左闭右开),并且i之前的字符串也可以拆分成一个或多个字典当中出现的单词(即dp[i]==true),那么dp[j]=true。
初始化:
dp[0]初始化成true,表示下标0之前的字符串可以拆分。
遍历顺序:
先遍单词的起始位置,在遍历单词的结束位置。
打印dp数组验证。
代码如下:
class Solution {public boolean wordBreak(String s, List<String> wordDict) {HashSet<String> set = new HashSet<>();//用来记录字典中出现过的单词for(String a : wordDict) {set.add(a);}boolean[] dp = new boolean[s.length()+1];//dp[j]==true表示在下标为j之前出现的单词可以拆分成一个或多个字典当中出现过的单词dp[0] = true;//除了dp[0]其他都初始化成false。for(int i = 1; i <= s.length(); i++) {//遍历每个单词的起始位置,注意实际起始位置是i-1。for(int j = i; j <= s.length(); j++) {//遍历每个单词结束位置的下一个位置if(set.contains(s.substring(i-1,j)) && dp[i-1] == true) {//如果这个单词出现在字典当中并且,这个单词之前的字符串可以拆分,那么从0到这个单词结束位置的字符串都可以拆分。在这个单词的结束位置打上标记。dp[j] = true;} }}return dp[s.length()];//如果最后一个位置的标记为true说明它之前的字符串可以拆分成字典 当中的单词}
}
总结
这道题是完全背包问题,要求的是,物品是否可以装满背包。
而背包容量的属性就是字符串,要求的就是单词是否可以组成字符串。