LeetCode热题(JS版) - 139. 单词拆分

224 阅读2分钟

题目描述

给定一个非空字符串s和一个包含非空单词列表的字典wordDict,判断s是否可以被分割成一个或多个在字典中出现的单词。

示例

示例1:

Input: s = "leetcode", wordDict = ["leet", "code"]
Output: true
Explanation: 可以将s分割为"leet""code"这两个单词。

示例2:

Input: s = "applepenapple", wordDict = ["apple", "pen"]
Output: true
Explanation: 可以将s分割为"applepen""apple"这两个单词。注意,我们只能使用字典中的单词一次。

示例3:

Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
Output: false
Explanation: 无法将s分割成字典中出现的单词。

解法

我们可以使用动态规划来解决这个问题。我们定义dp[i]表示s中前i个字符是否可以被拆分成一个或多个在字典中出现的单词。

那么对于每个位置i,我们需要枚举j从0到i-1,如果dp[j]=true并且从j到i的子串在字典中出现,那么就有dp[i]=true。

具体实现时,我们可以用一个set来存储字典中的所有单词,然后

  • 遍历s中的每个位置i,并在其中枚举j。
  • 如果dp[j]=true并且从j到i的子串在字典中出现,那么就有dp[i]=true。
  • 最终我们只需要返回dp[n]即可,其中n为字符串s的长度。

代码

function wordBreak(s: string, wordDict: string[]): boolean {
    const n = s.length;
    const dictSet = new Set(wordDict);
    const dp: boolean[] = new Array(n + 1).fill(false);
    dp[0] = true;

    for (let i = 1; i <= n; i++) {
        for (let j = 0; j < i; j++) {// 判断子串是否可以分解
            if (dp[j] && dictSet.has(s.substring(j, i))) {// 前一半可以,后一半存在
                dp[i] = true;
                break;
            }
        }
    }

    return dp[n];
}

时间复杂度

外层循环遍历整个字符串s,内层循环最多需要遍历s中的每个字符,因此总时间复杂度为O(n2)O(n^2)

空间复杂度

定义了一个长度为(n+1)的dp数组来记录前i个字符是否可以被拆分成一个或多个在字典中出现的单词,因此空间复杂度为O(n)O(n)。同时,使用了一个set来存储字典中的所有单词,空间复杂度为O(m)O(m),其中m为字典中单词的个数。因此,总的空间复杂度为O(n+m)O(n+m)