力扣第139题-单词拆分

109 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

前言

力扣第139题 单词拆分 如下所示:

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

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

示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true

一、思路

题目的意思很简单,就是使用 wordDict 中的单词拼凑出目标字符串 s (单词可以重复使用)。我很容易就想到使用 递归 来逐个使用 wordDict 中的单词,直到能够完整拼凑目标字符串。递归的代码如下所示:

image.png

但是非常可惜的是,遇到一些较长的测试用例会出现超时间的问题。

image.png

动态规划

那如何优化递归呢,以减少寻找的次数。一般来说可行的思路就是使用 动态规划 来优化递归。那如果做呢?

  1. 定义 dp[] 数组

我们注意到在递归中每次都要重新选取 wordDict 中的单词,那么要怎么优化这个事情呢?

我们可以设 dp[i] 为: s[0 ~ i-1] 的字符串 s1 是否可以由 wordDict 中的单词组成。

  1. 状态转移方程

我们假设知道了 dp[0 ~ i-1] 的值,我们可以枚举 s[j ~ i] 是否在 dictWord中,然后再根据 dp[j] 的值。只要在枚举的过程中出现了一次 true 就表示 s[0 ~ i-1] 可以由 dictWord 中的单词组成。通过上面的分析,很明显我们可以得出下面这个状态转移方程:

dp[i]=dp[j] && dictWord.contains(s[j ~ i−1]),(0 =< j <= i-1)

综上所述,使用递归的大致步骤如下所示:

  1. 使用 dp[i] 存储子字符串是否可以由 dictWord 组成
  2. 从前往后遍历,枚举 o ~ i 的子串匹配情况,就可以得到 dp[i+1] 的值了

二、实现

实现代码

实现代码与思路中保持一致,如下所示:

public boolean wordBreak(String s, List<String> wordDict){
    int len = s.length();
    Set<String> wordDictSet = new HashSet(wordDict);
    boolean[] dp = new boolean[len+1];
    dp[0] = true;   // 空字符串
    for (int i=1; i <= len; i++) {
        for (int j=0; j < i; j++) {
            if (dp[j] && wordDictSet.contains(s.substring(j, i))) {
                dp[i] = true;   // 有 true 就立刻去初始化下一个dp
                break;
            }
        }
    }
    return dp[s.length()];
}

测试代码

public boolean wordBreak(String s, List<String> wordDict){
    int len = s.length();
    Set<String> wordDictSet = new HashSet(wordDict);
    boolean[] dp = new boolean[len+1];
    dp[0] = true;   // 空字符串
    for (int i=1; i <= len; i++) {
        for (int j=0; j < i; j++) {
            if (dp[j] && wordDictSet.contains(s.substring(j, i))) {
                dp[i] = true;   // 有 true 就立刻去初始化下一个dp
                break;
            }
        }
    }
    return dp[s.length()];
}

结果

image.png

三、总结

感谢看到最后,非常荣幸能够帮助到你~♥

如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~