返回

LeetCode 139:单词拆分:巧解难题,逐层递进

见解分享

前言

在 LeetCode 的浩瀚题海中,“单词拆分”无疑是一道备受青睐的经典题目,它考察了算法、数据结构和字符串处理等多方面的综合能力。这道题目不仅能检验你的编程功底,还能拓展你的算法思维,为解决更复杂的编程问题奠定坚实的基础。

本文将带领你踏上征服 LeetCode 139 的征程,我们将逐层递进,层层剖析题目的内涵,并为你提供巧妙的解法,让你对这道题目有深入的理解和熟练的掌握。

问题

给你一个字符串 s 和一个单词字典 wordDict,判定 s 是否可以被切割成一个或多个字典中的单词。你可以假设字典中没有重复的单词。

示例:

输入:s = "leetcode", wordDict = ["leet", "code"]
输出:true
解释:s 可以拆分成 "leet""code"。

输入:s = "applepenapple", wordDict = ["apple", "pen"]
输出:true
解释:s 可以拆分成 "apple""pen""apple"。

输入:s = "catsanddog", wordDict = ["cats", "dog", "sand", "and"]
输出:false
解释:s 无法拆分成字典中的单词。

解题思路

解决这道题目的关键在于理解它的本质:将一个字符串拆分成一系列子串,判断每个子串是否在给定的单词字典中。根据此思路,我们可以采用自顶向下的递归回溯算法,具体步骤如下:

  1. 递归基: 当字符串 s 为空时,表示拆分成功,返回 true。
  2. 枚举单词长度: 从长度为 1 开始,逐一增加单词长度,直到达到字符串 s 的长度。
  3. 子串匹配: 对于每个单词长度,从字符串 s 的开头开始,截取长度为该长度的子串。判断该子串是否在单词字典中,如果是,则继续递归处理剩余字符串。
  4. 回溯: 如果某一分支的递归处理失败,则回溯到上一个分支,继续尝试其他可能的拆分方式。
  5. 记录结果: 为避免重复计算,可以使用哈希表记录已经拆分成功的子字符串。

代码实现(Python)

def wordBreak(s, wordDict):
    """
    :type s: str
    :type wordDict: List[str]
    :rtype: bool
    """
    # 存储已经拆分成功的子字符串
    memo = {}

    def dfs(start):
        # 非递归出口
        if start == len(s):
            return True

        # 已经计算过的子串
        if start in memo:
            return memo[start]

        # 枚举单词长度
        for end in range(start + 1, len(s) + 1):
            # 截取子串
            substring = s[start:end]
            # 判断子串是否在字典中
            if substring in wordDict:
                # 递归处理剩余字符串
                if dfs(end):
                    memo[start] = True
                    return True

        # 所有情况都失败
        memo[start] = False
        return False

    return dfs(0)

时间复杂度

最坏情况下,每个子串都需要尝试所有可能的拆分方式,因此时间复杂度为 O(2^n),其中 n 为字符串 s 的长度。

空间复杂度

最坏情况下,每个子串都需要存储在哈希表中,因此空间复杂度为 O(n),其中 n 为字符串 s 的长度。

总结

恭喜你攻克了 LeetCode 139 题!通过本文的深入讲解和巧妙的解法,相信你对单词拆分问题的理解又上了一个台阶。

解决算法问题时,逐层递进的思维方式至关重要。将复杂问题分解成一个个小问题,再逐步解决,往往能事半功倍。此外,掌握高效的数据结构和算法也是提升编码能力的关键。

希望本文能助你提升算法思维,攻克更多编程难题!