dp--139. 单词拆分/medium 理解度B

2024-01-09 08:43:30

1、题目

给你一个字符串 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
  • swordDict[i] 仅由小写英文字母组成
  • wordDict 中的所有字符串 互不相同
Related Topics
  • 字典树
  • 记忆化搜索
  • 数组
  • 哈希表
  • 字符串
  • 动态规划

2、题目分析

动态规划核心用处是求最值,当然也可用于判断是否存在可行性方案这类的问题。因为是先有多个可行性方案,才会衍生出最值问题。
故该问题也可以用动态规划,虽然题目只求截止到最后一个字母,字典中出现的单词是否能拼接出该字母及前面所组成的字符串 s。

动态规划的解决套路可分为 2 步,①基于问题能定义出状态,②状态间具备动态规划的三个特性

①基于问题定义出状态:问题是“求截止到最后一个字母所组成的字符串,是否可由字典拼接而成”。则状态就可以定义为“截止到某个字母所组成的字符串,是否可由字典拼接而成”。因为这样,当流转到最终状态时,即可得到问题的答案。

②状态间具备动态规划的三个特性

  1. 重复子问题且重复策略
    重复子问题:截止到每个字母,问题都是“字典中出现的单词是否能拼接出截止到该字母所组成的字符串”
  2. 最优子结构
    重复策略&最优子结构:针对每个子问题,策略都是以该字母做为尾字符,匹配字典中的每个单词,然后往前推到该单词加入前的状态,若单词加入前的状态已被求得解。则当前状态就有解。否则,当前状态也无解。
  3. 无后效性
    更前的状态不影响当前状态:从2、最优子结构可知,我们求当前状态时,只关注单词加入前的状态,而无需关注单词加入前的状态是如何被一步步推导出来的。
    后面的状态不影响当前状态:从2、最优子结构可知,当前状态完全有前面的状态转移得到,跟后面的状态没有关系

3、解题步骤

1、dp[i] 表示截止到第 i 个字符,所组成的字符串,是否可由字典拼接而成。
2、初始化,dp[0]表示空串,默认可由字典拼接而成
3、迭代处理截止到的每个字符所组成的字符串,判断字符串是否可由字典拼接而成
4、判断当前状态是否可达。即是否存在前面的某个状态,结合上本单词 word,转移到当前状态
5、若以当前字符为尾字符,在字典中找到了匹配字符串的后缀。再因为开头已经判断了结合该单词前的那个状态dp值为1,即前面状态可达,故当前状态可达。

4、复杂度最优解代码示例

    public boolean wordBreak(String s, List<String> wordDict) {
        // 1、dp[i] 表示截止到第 i 个字符,所组成的字符串,是否可由字典拼接而成。
        int[] dp = new int[s.length() + 1];
        // 2、初始化,dp[0]表示空串,默认可由字典拼接而成
        dp[0] = 1;
        for (int i = 0; i < s.length(); i++) {
            // 3、迭代处理截止到的每个字符所组成的字符串,判断字符串是否可由字典拼接而成
            int flag = 0;
            for (String word : wordDict) {
                // 4、判断当前状态是否可达。即是否存在前面的某个状态,结合上本单词 word,转移到当前状态
                if (i - word.length() + 1 < 0 || dp[i - word.length() + 1] == 0) {
                    // 前面不存在某一状态 结合该单词 来达到当前状态。有以下2种场景
                    // 判断单词是否越界 || 加上本单词前,对应的上个状态还未可达
                    continue;
                }
                String sWord = s.substring(i - word.length() + 1, i + 1);
                if (sWord.equals(word)) {
                    // 若以当前字符为尾字符,在字典中找到了匹配字符串的后缀。再因为开头已经判断了结合该单词前的那个状态dp值为1,即前面状态可达,故当前状态可达。
                    flag = 1;
                }
                dp[i + 1] = flag;
            }
        }
        return dp[s.length()] == 1;
    }

5、抽象与扩展

通用动态规划的解法,见标题二

文章来源:https://blog.csdn.net/fujuacm/article/details/135408092
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。