答案:
func wordBreak(s string, wordDict []string) bool {
wordDictSet := make(map[string]bool)
for _, w := range wordDict {
wordDictSet[w] = true
}
dp := make([]bool, len(s) + 1)
dp[0] = true
for i := 1; i <= len(s); i++ {
for j := 0; j < i; j++ {
if dp[j] && wordDictSet[s[j:i]] {
dp[i] = true
break
}
}
}
return dp[len(s)]
}
给定一个字符串 s 和一个字典 wordDict,判断 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
wordDictSet := make(map[string]bool)
for _, w := range wordDict {
wordDictSet[w] = true
}
这里,代码首先将 wordDict 转换为一个哈希集合 wordDictSet。这样做的好处是,当我们需要检查一个单词是否在字典中时,哈希集合的查找时间复杂度是 O(1),这比在列表中查找要快得多。
2. 动态规划
接下来,代码使用动态规划来解决这个问题。
dp := make([]bool, len(s) + 1)
dp[0] = true
dp是一个布尔数组,其中dp[i]表示从字符串s的开始到位置i-1的子串是否可以被拆分为字典中的单词。由于空字符串可以被视为已拆分,所以dp[0]被初始化为true`。
3. 动态规划的主要逻辑
for i := 1; i <= len(s); i++ {
for j := 0; j < i; j++ {
if dp[j] && wordDictSet[s[j:i]] {
dp[i] = true
break
}
}
}
这是一个嵌套循环。外层循环遍历字符串 s 的所有位置(从 1 到 len(s)),而内层循环则从当前位置 i 向前遍历,检查所有可能的子串。
对于每个子串 s[j:i](即从位置 j 到 i-1 的子串),代码检查以下两个条件:
dp[j]
是否为true
,这意味着从字符串的开始到位置j-1
的子串可以被拆分为字典中的单词。s[j:i]
是否在wordDictSet
中,这意味着这个子串本身是一个字典中的单词。如果这两个条件都满足,那么从字符串的开始到位置i-1
的子串也可以被拆分为字典中的单词,因此dp[i]
被设置为true
,并且内层循环可以立即中断(因为我们已经找到了一个有效的拆分)。