LeetCode题练习与总结:单词拆分--139

165 篇文章 0 订阅
99 篇文章 0 订阅

一、题目描述

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true

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

示例 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,长度为 s.length() + 1
  • 将 dp[0] 设置为 true,因为空字符串可以被看作是由空单词列表拼接而成。

2. 填充动态规划数组

  • 遍历字符串 s 的每个可能的结束位置 i,从 1 到 s.length()
  • 对于每个 i,再进行一次内部循环,遍历所有可能的前一个位置 j,从 0 到 i-1
  • 在内部循环中,检查 dp[j] 是否为 true,且 s.substring(j, i) 是否在字典 wordDict 中。
  • 如果上述条件成立,说明从 j 到 i 的子串可以被字典中的单词拼接而成,因此将 dp[i] 设置为 true

3. 返回结果

  • 动态规划数组填充完毕后,dp[s.length()] 的值即为所求,它表示整个字符串 s 是否可以被字典中的单词拼接而成。
  • 返回 dp[s.length()] 作为函数的输出。

这个动态规划解法的核心思想是将大问题分解为小问题,通过检查所有可能的前缀来逐步构建出整个字符串是否可以被拼接的答案。

三、具体代码

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> wordSet = new HashSet<>(wordDict);
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;

        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j < i; j++) {
                if (dp[j] && wordSet.contains(s.substring(j, i))) {
                    dp[i] = true;
                    break;
                }
            }
        }

        return dp[s.length()];
    }
}

四、时间复杂度和空间复杂度

1. 时间复杂度
  • 将列表 wordDict 转换为哈希集合 wordSet 的时间复杂度是 O(k),其中 k 是字典中单词的数量。这是因为需要对每个单词进行哈希运算。
  • 初始化布尔数组 dp 的时间复杂度是 O(n),其中 n 是字符串 s 的长度。
  • 双层循环的时间复杂度是 O(n^2),因为外层循环执行了 n 次,内层循环在最坏情况下也可能执行 n 次。
  • substring 操作的时间复杂度是 O(k),其中 k 是子字符串的长度。在最坏情况下,k 可以达到 n,但通常情况下,k 会有一个上限,即字典中最长单词的长度。

综上所述,总的时间复杂度是 O(n^2 * m),其中 m 是字典中单词的平均长度。这是因为对于每个子字符串,我们需要检查它是否在字典中,这个操作的时间复杂度是 O(m)。

2. 空间复杂度
  • 哈希集合 wordSet 的空间复杂度是 O(k),其中 k 是字典中单词的数量。
  • 布尔数组 dp 的空间复杂度是 O(n),其中 n 是字符串 s 的长度。

因此,总的空间复杂度是 O(n + k),即由动态规划数组和哈希集合构成的空间需求。

五、总结知识点

  1. 动态规划:这是一种算法设计技术,用于解决优化问题。它将问题分解为更小的子问题,并通过组合子问题的解来解决原始问题。在这个问题中,dp 数组用于存储子问题的解,即字符串的前缀是否可以被字典中的单词拼接而成。

  2. 哈希集合HashSet 是 Java 中的一个集合实现,用于存储不重复的元素,并且可以快速判断一个元素是否存在于集合中。在这个问题中,wordSet 用于存储字典中的单词,以便快速检查一个子字符串是否是字典中的单词。

  3. 字符串操作substring 方法是 Java String 类的一个方法,用于提取字符串中的一个子串。在这个问题中,它用于提取从位置 j 到 i 的子字符串,检查它是否在字典中。

  4. 数组的初始化:代码中使用 new boolean[s.length() + 1] 初始化了一个布尔数组 dp,所有元素默认为 false。然后,dp[0] 被显式设置为 true,因为空字符串可以被看作是由空单词列表拼接而成。

  5. 双层循环:外层循环遍历字符串 s 的每个可能的结束位置 i,内层循环遍历所有可能的前一个位置 j。这种结构用于填充动态规划数组 dp

  6. 递推关系:动态规划的核心是找到子问题之间的递推关系。在这个问题中,dp[i] 的值取决于 dp[j]j < i)的值和子字符串 s.substring(j, i) 是否在字典中。

  7. 边界条件:在动态规划中,通常需要设置边界条件。在这个问题中,dp[0] 被设置为 true,这是递推的起始条件。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

  • 29
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一直学习永不止步

谢谢您的鼓励,我会再接再厉的!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值