小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
问题是从一个备选词库找到一系列字母组合,将这些字母组合按一定顺序组合成目标单词。还是通过图来解释问题。
目标单词 adcdef 数组为 [ad,abc,cd,def,abcd],这里我们需要从前向后一个个截取单词,不可以从中间来截取单词。所以这里如果用 cd 去截取字符串剩余字母顺序就被打乱了。
我们可以看每条分支也就是一条一条路径来看,首先是最左侧的路径为 [ab,cd] 剩余的 ef 在 [ad,abc,cd,def,abcd] 中找不到对应字母组合所以这两条路径在叶子节点返回的是 ef 返回 false 当两个叶子节点返回不同值相遇false 和 true 只要有一个 true 就返回为 true。在看第二条路径,[abc,def] 满足条件,剩余为 ''。
上面例子中,每一个分支都是返回 false,也就是在给出数组将这些元素任意组合都无法得到目标单词。
const canConstructor = (target,wordBank)=>{
if(target === ''){
return true;
}
for(let word of wordBank){
if(target.indexOf(word) === 0 ){
const suffix = target.slice(word.length);
if(canConstructor(suffix,wordBank) === true){
return true;
}
}
}
return false;
}
target.indexOf(word) === 0 判断是目标单词前几个字母是否存在对应的字母组合存 wordBank 。
const suffix = target.slice(word.length) 将找到的字母组合前缀截取掉,剩下字符串作为输入到函数canConstructor。
优化
优化算法还是通过缓存方式将结果缓存起来,在下一次调用时候直接读取缓存避免重复计算。
const canConstructorWithMemo = (target,wordBank,memo={})=>{
if(target in memo) return memo[target]
if(target === ''){
return true;
}
for(let word of wordBank){
if(target.indexOf(word) === 0 ){
const suffix = target.slice(word.length);
if(canConstructorWithMemo(suffix,wordBank,memo) === true){
memo[target] = true
return memo[target];
}
}
}
memo[target] = false;
return memo[target];
}
const res = canConstructor('abcdef',['ab','abc','cd','def','abcd'])
console.log(res);
const res1 = canConstructor('skateboard',['bo','rd','ate','t','ska','sk','boar'])
console.log(res1);
const res2 = canConstructorWithMemo('eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeef',[
'e',
'ee',
'eee',
'eeee',
'eeeee',
'eeeeee',
'eeeeeee',
'eeeeeeee',
'eeeeeeeeee',
])
console.log(res2)