题目
给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
示例
输入: s = “leetcode”, wordDict = [“leet”, “code”]
输出: true
解释: 返回 true 因为 “leetcode” 可以由 “leet” 和 “code” 拼接成。
解析
首先这道题也可以用回溯来做,这里先不管回溯了,只说动态规划;
首先翻译一下题意就是:字符串s是背包,后面那个数组是物品,且是一个完全背包问题,那么用动规五部曲分析下:
1.确认dp数组及其含义
dp[i]:字符串长度为i的话,dp[i]表示是否可以拼出来给定字符串
2.确定递推公式
在 j < i 的情况下,如果确定dp[j] 是true,且 [j, i] 这个区间的子串出现在字典里,那么dp[i]一定是true。
3.初始化
dp[0] = true,因为是根据它推出来的,不然后面的都成了false了
4.遍历顺序
趁着在这可以复习一遍之前背的遍历顺序:
如果求组合数就是外层for循环遍历物品,内层for循环遍历背包。
如果求排列数就是外层for循环遍历背包,内层for循环遍历物品。
而对于本题来说,最后求的是能否构造出xxx,其实是一个排列,比如:
s = “applepenapple”, wordDict = [“apple”, “pen”]
那么一定是物品的组合为: “apple” + “pen” + “apple” 才能组成 “applepenapple”。
func wordBreak(s string, wordDict []string) bool {
// 单词可以重复使用,完全背包问题
// 首先明确,s是背包,后面的那个数组是物品
wordDictMap := make(map[string]bool)
for _, val := range wordDict { // 注意这里遍历的是数组,每个元素可能是一个单词
wordDictMap[val] = true // map先全部初始化为true
}
dp := make([]bool, len(s)+1)
dp[0] = true //默认一定是true
for i := 1; i <= len(s); i++ { // 遍历背包,0没有什么遍历的必要了
for j := 0; j < i; j++ { // 遍历物品(直接遍历小于当前背包容量的情况),想想有没有等于,虽然加上等也过了,但下面的判断条件里,后半个就没有意义了
if dp[j] && wordDictMap[s[j:i]] {
dp[i] = true
}
}
}
return dp[len(s)]
}