Leetcode刷题笔记46:动态规划8(139.单词拆分)

191 阅读2分钟

导语

leetcode刷题笔记记录,本篇博客是动态规划部分,主要记录题目包括:

Leetcode 139. 单词拆分

题目描述

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。

注意: 不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。

 

示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以由 "leet""code" 拼接成。

示例 2:

输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以由 "apple" "pen" "apple" 拼接成。
     注意,你可以重复使用字典中的单词。

示例 3:

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

 

提示:

  • 1 <= s.length <= 300
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 20
  • s 和 wordDict[i] 仅由小写英文字母组成
  • wordDict 中的所有字符串 互不相同

解法

使用动态规划转换为完全背包问题:要表示的字符串为背包,每个单词为物品,那么应用动规五部曲如下:

  1. dp数组含义:dp[i] 表示字符串 s 的前 i 个字符能否被字典 wordDict 中的单词拼接而成,是bool类型的值;
  2. 递推公式,结合下面的示意图来理解:如果dp[j]为True,而且[j:i]组成的子串是一个单词,那么dp[i]更新为True;
  3. 初始化:dp[0]为True,其他均为False;
  4. 遍历顺序:由于这里需要求解的是组成方式,那么求解的是“排列”的情形,应该先遍历背包,后遍历物品;
  5. 打印dp数组。

image.png

代码如下:

from typing import List

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        # 初始化动态规划数组 dp,长度为 len(s) + 1。
        # dp[i] 表示字符串 s 的前 i 个字符能否被字典 wordDict 中的单词拼接而成。
        # dp[0] 是 True,因为空字符串总是可以被拼接。
        dp = [True] + [False] * len(s)
        
        # 外层循环:遍历 s 的所有子字符串。
        for i in range(1, len(s) + 1):
            
            # 内层循环:遍历当前子字符串的所有可能的起始位置。
            for j in range(i):
                
                # 获取当前子字符串。
                word = s[j:i]
                
                # 检查当前子字符串是否在 wordDict 中,并且 dp[j] 是 True。
                # 如果是这样,那么 s 的前 i 个字符可以被拼接,所以设置 dp[i] 为 True。
                if word in wordDict and dp[j]:
                    dp[i] = True
                    
        # 返回 dp[len(s)],它表示整个字符串 s 能否被拼接。
        return dp[len(s)]

易错点

题目中说是拆分为一个或多个在字典中出现的单词,所以这是完全背包。需要讨论两层for循环的前后顺序。根据[代码随想录]中的总结:

如果求组合数就是外层for循环遍历物品,内层for遍历背包如果求排列数就是外层for遍历背包,内层for循环遍历物品

部分题目如下:

  1. 求组合数:动态规划:518.零钱兑换II (opens new window)
  2. 求排列数:动态规划:377. 组合总和 Ⅳ (opens new window)动态规划:70. 爬楼梯进阶版(完全背包) (opens new window)
  3. 求最小数:动态规划:322. 零钱兑换 (opens new window)动态规划:279.完全平方数(opens new window)

而本题其实我们求的是排列数,假设s = "applepenapple", wordDict = ["apple", "pen"]。"apple", "pen" 是物品,那么物品的组合一定是 "apple" + "pen" + "apple" 才能组成 "applepenapple"。强调物品之间顺序。所以说,本题一定是先遍历背包,再遍历物品。