86.单词拆分

46 阅读1分钟

题目链接

给你一个字符串 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)