给你一个字符串
s和一个字符串列表wordDict作为字典。如果可以利用字典中出现的一个或多个单词拼接出s则返回true。注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
解法1 记忆化dfs
思路
用 dfs 尝试的话从起点开始,一步步切割子串,判断是否能走到终点。
切割的 dfs 函数从索引 start 开始,字符串 s[start:] 是否能被拆成一个或多个字典中的词。
代码
function wordBreak(s: string, wordDict: string[]): boolean {
const memo = new Map();
const wordSet = new Set(wordDict);
const canBreak = (start) => {
if (start === s.length) return true;
if (memo.has(start)) return memo.get(start);
for (let end = start + 1; end <= s.length; end++) {
const word = s.substring(start, end);
if (wordSet.has(word) && canBreak(end)) {
memo.set(start, true);
return true;
}
}
memo.set(start, false);
return false;
}
return canBreak(0);
};
时空复杂度
时间复杂度:O(n^2)
空间复杂度:O(n + m)
解法2 动态规划
思路
首先思考 dp 数组代表什么含义:
dp[i] 表示:字符串 s 的前 i 个字符 s[0...i-1] 是否可以被字典中的单词完全拆分。
而状态转移计算 dp[i] 的值,基于前面的 dp[j] 和 s[j:i] 的判断。
代码
function wordBreak(s: string, wordDict: string[]): boolean {
const dp = new Array(s.length + 1).fill(false);
const wordSet = new Set(wordDict);
dp[0] = true;
for (let i = 1; i <= s.length; i++) {
for (let j = 0; j < i; j++) {
if (dp[j] && wordSet.has(s.substring(j, i))) {
dp[i] = true;
break;
}
}
}
return dp[s.length];
};
时空复杂度
时间复杂度:O(n^2)
空间复杂度:O(n + m)