在问 · 算法题解

140. 单词拆分 II

困难回溯回溯枚举切分力扣原题 ↗

题目描述

输入字符串 s 和字典 wordDict。在 s 中插入空格,把它分成若干单词,要求每个单词都在字典里。字典里的单词可以重复使用。

每种分法写成一个句子:单词之间用一个空格隔开。返回所有可能的句子,顺序不限;一种都没有时返回 []。

示例 1
输入:s = "catsanddog", wordDict = ["cat","cats","and","sand","dog"]
输出:["cat sand dog","cats and dog"]

解释:可以分成 cat sand dog 或 cats and dog。

示例 2
输入:s = "pineapplepenapple"
wordDict = ["apple","pen","applepen","pine","pineapple"]
输出:["pine apple pen apple","pine applepen apple","pineapple pen apple"]

解释:共有三种分法,其中 apple 可以重复使用。

示例 3
输入:s = "catsandog", wordDict = ["cats","dog","sand","and","cat"]
输出:[]

解释:无法把整个字符串分成字典里的单词。

数据范围

  • 1 <= s.length <= 20
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 10
  • s 和 wordDict[i] 只含小写英文字母。
  • wordDict 中没有重复单词。

解题思路:回溯枚举切分

回溯。dfs(start) 表示 s[0:start] 已经切好并放在 path 里。枚举下一个单词的结束位置 end,只要 s[start:end] 在字典里,就把它放进 path 并递归 dfs(end),返回后弹出。start 等于 len(s) 时,用空格拼接 path 记录一个句子。

为什么正确

path 中的单词依次拼接恰好等于 s[0:start],且每个都在字典中。

复杂度分析

最坏情况下句子数量是指数级,时间 O(2^n · n);递归深度 O(n)。

易错点

句子里的空格只能出现在单词之间,用 join 拼接最稳妥;另外不要在找到一个句子后就停,要枚举完所有 end。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> List[str]:
        words = set(wordDict)
        res, path = [], []

        def dfs(start):
            if start == len(s):
                res.append(' '.join(path))
                return
            for end in range(start + 1, len(s) + 1):
                w = s[start:end]
                if w in words:
                    path.append(w)
                    dfs(end)
                    path.pop()

        dfs(0)
        return res

载入编辑器,自己跑一遍 →

其他解法

回溯法空间小但会重复搜索无解或共享的后缀;记忆化用空间换时间,每个后缀只展开一次。

解法 2:记忆化搜索 时间 O(n·2ⁿ),空间 O(n·2ⁿ)

suffix(start) 返回从 start 开始的后缀的所有拆分句子。枚举第一个单词 s[start:end],若在字典中,就把它拼到 suffix(end) 的每个句子前面。每个 start 的结果只算一次并缓存。

同一后缀被多条前缀路径共用时,避免重复搜索。最坏情况输出本身是指数级,缓存也会占用同量级空间。

from functools import lru_cache
from typing import List

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> List[str]:
        words = set(wordDict)
        n = len(s)

        @lru_cache(None)
        def suffix(start):
            if start == n:
                return [[]]
            res = []
            for end in range(start + 1, n + 1):
                w = s[start:end]
                if w in words:
                    for rest in suffix(end):
                        res.append([w] + rest)
            return res

        return [' '.join(p) for p in suffix(0)]

回溯 专题的其他题目

查看 回溯 全部题目 →