「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」。
题目
给你一个字符串 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,leetcode
能否由 ["leet", "code"]
拼接而成,可以拆分为:
-
看
l
是否为单词表的单词,以及剩下的eetcode
能否被单词表拼接而成 -
看
le
是否为单词表的单词,以及剩下的etcode
能否被单词表拼接而成 -
看
lee
是否为单词表的单词,以及剩下的etcode
能否被单词表拼接而成 -
...类推
如果上述有一条满足,则表示对应的字符串,可以有单词表拼接而成;
那么上述的解法,可以用 回溯算法
处理:
-
1、定义函数
dfs(start)
,表示当前位置start
开始的字符串,能否被单词表拼接而成 -
2、从
start
开始,移动指针到startEnd
,如果这区间的字符串,在单词表中,则考虑dfs(startEnd + 1)
-
3、如果当前
start
到了字符串尾部,则返回为true
DFS解法
/**
* @param {string} s
* @param {string[]} wordDict
* @return {boolean}
*/
var wordBreak = function(s, wordDict) {
const n = s.length;
// 先将单词表转换为set结构,方便查询单词
const set = new Set(wordDict);
// 判断start开始的字符串,能够被单词表拼接
const dfs = start => {
// 到了字符串尾部,则认为可以
if(start == n) return true
// 当前的部分字符串
let tempS = '';
for(let i = start; i < n; i++) {
// 移动指针,将字符串累加
tempS += s.charAt(i);
// 如果单词表有该字符串,并且后面的也能被单词边拼接而成,则从 start 开始的都可以被单词表拼接
if(set.has(tempS) && dfs(i + 1)) {
return true;
}
}
// 上面没有返回,则 start 开始的,不能被单词表拼接
return false;
}
// 从0开始
return dfs(0);
};
上述解法,在遇到case
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab" ["a","aa","aaa","aaaa","aaaaa","aaaaaa","aaaaaaa","aaaaaaaa","aaaaaaaaa","aaaaaaaaaa"]
运行超时了
分析:
上面的算法,其实存在很多重复的计算,例如对于前半部分 a
,在单词表中,后续需要计算 start = 2, 3, 4...
,最后由于尾部存在 b
,无法拼接;而后就会进行 前半部分为 aa
,在单词表中,后续需要计算 start = 3, 4, 5 ...
上述重复的计算是不必要的,可以将已计算的结果存下来,下次直接使用,通常这称为 DFS记忆优化
记忆优化DFS
var wordBreak = function(s, wordDict) {
const n = s.length;
const set = new Set(wordDict);
// 将已计算的结果存下来,index 为 key,memo[index] 为计算结果
const memo = new Array(n);
const dfs = start => {
if(start == n) return true;
// 如果已计算,则直接返回
if(memo[start] !== undefined) return memo[start];
let tempS = '';
for(let i = start; i < n; i++) {
tempS += s.charAt(i);
if(set.has(tempS) && dfs(i + 1)) {
// 将结果存下来
memo[start] = true;
return true;
}
}
// 将结果存下来
memo[start] = false;
return false;
}
return dfs(0);
};
小结
DFS
算法往往在遇到极端情况时,会出现超时、重复计算的问题,而使用额外数据结构,将已计算的结果存下来,能够避免这类问题。
LeetCode 👉 HOT 100 👉 单词拆分 - 中等题 ✅
合集:LeetCode 👉 HOT 100,有空就会更新,大家多多支持,点个赞👍
如果大家有好的解法,或者发现本文理解不对的地方,欢迎留言评论 😄