
思路:
刚开始看的时候没有思路,但我看给的样例,可以多次遍历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]。 -
"最后一段"就是从索引
j到i-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)在字典里 → 从j到i-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。