题目链接:
题目描述:
给你一个字符串 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 中的所有字符串 互不相同
解题思路:
翻译题目可以理解为能不能找出wordDict中的词的一种排列,使这个排列与s相同。这里之所以说的是排列不是组合,是因为wordDict同一堆元素的组合只能取到一种,而s可能对应这些元素的另一种组合,这样就会漏算。比如,s = [2, 1],wordDict = [1, 2]如果取组合的话,wordDict可能只能取到[1, 2]这一种组合(3个元素的组合),显然这个组合与s并不相同(顺序不同);但如果讨论的是排列数,那么wordDict就可取到[1, 2],[2, 1],这样第二项就可以跟s匹配了
从wordDict中找到特定元素放到一起,且这些元素满足一定的条件,这样的描述满足背包问题的特征。容量是字符串的长度,物品是字典,每件物品可重复取,所以是完全背包。dp[j]表示字符串到长度j可不可被拆分,true or false。
这里说一下求组合数量得先遍历物品再遍历容量的原因:
假设dp[0]=1,dp[1]=3。那么先遍历物品再遍历容量的话就只能先到取1,再取3,只能取到{1, 3}这个组合,而不能取到{3, 1}这个组合。即dp[4]中对于1和3就只有{1, 3}这种情况
再说一下求排列数量得先遍历容量再遍历容物品的原因:
假设dp[0]=1,dp[1]=3。那么先遍历容量再遍历物品的话每个容量都要经过1和3的计算,即会先对取1的情况进行决策(可以取到{1, 3}),再对取3的情况进行决策(可以取到{3, 1})。即dp[4]中对于1和3有{1, 3}和{3, 1}2种情况.
dp四部曲:
确定dp数组:dp[j]表示字符串到长度j可不可被拆分,true or false
确定递推关系dp[j] =dp[j] or (dp[j - len(dict[i])] and word[j - len(dict[i]) : j]==dict[i])
初始化dp数组:dp[0] =true,只是为了推导方便,因为其他dp都是从dp[0]推过去的,其他为false
遍历顺序:由于讨论的是排列,所以外层遍历容量(s),内层遍历物品(wordDict),均采用顺序遍历
代码如下:
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
dp = [False] * (len(s) + 1)
dp[0] = True
for j in range(len(s) + 1):
for word in wordDict:
if(j >= len(word)):
dp[j] = dp[j] or (dp[j - len(word)] and s[j - len(word) : j] == word)
return dp[len(s)]