139. 单词拆分
给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
说明:
拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
示例:
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false
分析:
(1)匹配单词可以想到使用哈希表来存储 单词/布尔值 键值对来表示该字符串是否是wordDict中的单词。
(2)思考如何将s进行分隔,以便使用哈希表查询真值。可以使用动态规划来解决这个问题。dp[i] = true 表示s的前i个字符可以被wordDict中的单词匹配(注意:可以是一个单词,也可以是多个单词)。转移方程可以帮我们解决匹配多个单词的问题:(dp[i] = dp[j] && wordDictSet[s[i:j]])。
代码(Golang):
func wordBreak(s string, wordDict []string) bool {
wordDictSet := make(map[string]bool)
for _, word := range wordDict {
wordDictSet[word] = true
}
dp := make([]bool, len(s) + 1)
dp[0] = true
for i := 1; i < len(s) + 1; i++ {
for j := 0; j < i; j++ {
if dp[j] && wordDictSet[s[j:i]] {
dp[i] = true
break
}
}
}
return dp[len(s)]
}
小结:
这道题中使用了动态规划,就可以将一个看起来不好入手的问题分解成几个简单的问题:
-
确定字符串的一部分是否匹配成功。
-
将这部分字符串的真值 状态传递下去。
-
包含这部分字符串的大字符串的状态由小字符串 及 另一部分小字符串的状态决定。
-
大的字符串的状态又被转移到更大的字符串。
做完之后思考这道题的解题步骤,再次感叹动态规划思想的精妙。