单词拆分----dp

思路:

刚开始看的时候没有思路,但我看给的样例,可以多次遍历wordDict看。。。好像不太对准备看看题解。

首先需要知道这道题的dp的公式代表这什么,dp[i]表示 字符串s从起始位置到位置i,能否被被拆分成字典中的单词(可能是一个,也可能是多个)。

比如dp[3]就是问s[1],s[2],s[3] 这三个字符能否拆分成字典中单词的组合。

于是我们下一步需要思考一下代码应该怎么写:

复制代码
class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> wset = new HashSet<>(wordDict);
        int n = s.length();
        boolean[] dp = new boolean[n + 1];
        dp[0] = true; // 空字符串可以拼出来

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

        return dp[n];
    }
}

s.substring(a,b)中b是不包含的也就是说真实包含的数组下标是,a-----b-1

解释一下dp[0]为什么等于true,因为在拆分的时候

s = "leetcode"wordDict = ["leetcode"]

  • i = 8,内层循环 j = 0

    • dp[0]true,并且 s.substring(0, 8) = "leetcode" 在字典中 → 条件成立,dp[8]=true

如果没有 dp[0]=true,那么即使整个字符串是一个单词,也无法通过 j=0 的情况识别出来,所有以 j=0 开头的判断都会失败,导致错误结果。

我们一步一步把 if (dp[j] && wordSet.contains(s.substring(j, i)))dp[i] = true 之间的逻辑关系拆开讲清楚。


1. 先明确 dp 数组的含义

在这段代码中:

  • dp[k] 表示:原字符串 s 的前 k 个字符 (即 s[0]s[k-1] 这一段)能否被拆分成字典中的单词

  • 例如 dp[3] 就是问 s[0],s[1],s[2] 这三个字符能否拆分成字典里的单词组合。


2. 我们要算什么?

外层循环 i 从 1 到 n,是要依次判断 dp[1], dp[2], ... dp[n] 是否为真。

当我们在计算 dp[i] 时,我们要问:i 个字符能不能被拆分成字典里的单词?

一种思考方式是:把前 i 个字符想象成 "前面一段" + "最后一段"

  • "前面一段"的长度是 j(0 ≤ j < i),也就是 s[0..j-1]

  • "最后一段"就是从索引 ji-1 的子串,长度为 i-j,也就是 s.substring(j, i)

如果能找到某个 j,使得:

  • 前面一段 (s[0..j-1]) 可以被拆分成字典单词(即 dp[j] == true),并且

  • 最后一段 (s.substring(j, i)) 本身就在字典里(wordSet.contains(...)),

那么整个 s[0..i-1] 就肯定可以被拆分(前面一段按 dp[j] 的方式拆,最后一段直接作为单词)。


3. 所以 if 条件里的 dp[j] && wordSet.contains(...) 就是检查这个假设是否成立

  • dp[j] == true → 前 j 个字符可拆分。

  • s.substring(j, i) 在字典里 → 从 ji-1 这一段直接是一个单词。

如果两者都满足,那么 s[0..i-1] 就找到了一个拆分方案,因此 dp[i] 应该为 true


4. 为什么 dp[i] = true 只写一次就够了?

因为一旦我们找到了一个有效的 j,就证明了 dp[i] 可以为 true,不需要继续找其他 j(反正结果已经是真了)。所以代码里 break 跳出内层循环。


5. 举个例子(用具体数字帮助理解)

s = "leetcode"wordDict = ["leet","code"]

  • 我们要算 dp[8](整个字符串长度 8)。

  • 内层循环 j 从 0 到 7:

    • j = 4 时:

      • dp[4] 是什么?前面算过,"leet" 可拆分 → dp[4]=true

      • s.substring(4, 8) 是什么?索引 4~7 → "code",在字典中。

    • 条件成立 → 给 dp[8] = true

这就是 "前 8 个字符可以拆成 (前4个字符:leet) + (4..8:code)"。


总结一句话:

if (dp[j] && wordSet.contains(s.substring(j, i))) 就是在问:"能不能把前 i 个字符切成两半,使得前半部分可拆分(dp[j]=true),后半部分本身就是一个单词(在字典里)? "

如果能,那么 dp[i] 就是 true

相关推荐
智者知已应修善业2 小时前
【51单片机LED闪烁10次数码管显示0-9】2023-12-14
c++·经验分享·笔记·算法·51单片机
智者知已应修善业2 小时前
【51单片机2按键控制1个敞亮LED灯闪烁和熄灭】2023-11-3
c++·经验分享·笔记·算法·51单片机
AI算法沐枫2 小时前
大模型 | 大模型之机器学习基本理论
人工智能·python·神经网络·学习·算法·机器学习·计算机视觉
吃着火锅x唱着歌2 小时前
LeetCode 1019.链表中的下一个更大节点
算法·leetcode·链表
凌波粒2 小时前
LeetCode--404.左叶子之和(二叉树)
算法·leetcode·职场和发展
paeamecium3 小时前
【PAT甲级真题】- A+B in Hogwarts
c++·算法·pat考试·pat
青山师3 小时前
二叉树与BST深度解析:遍历算法与平衡策略
数据结构·算法·面试·二叉树·算法与数据结构·java面试·数据结构与算法分析
绝知此事3 小时前
【算法突围 03】核心算法思想:分治/递归/动态规划与 LeetCode 高频真题解析
算法·leetcode·面试·动态规划