Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions problems/0139.单词拆分.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ public:

## 拓展

### 1. 关于遍历顺序

关于遍历顺序,再给大家讲一下为什么 先遍历物品再遍历背包不行。

这里可以给出先遍历物品再遍历背包的代码:
Expand Down Expand Up @@ -249,6 +251,67 @@ public:

如果大家对这里不理解,建议可以把我上面给的代码,拿去力扣上跑一跑,把dp数组打印出来,对着递推公式一步一步去看,思路就清晰了。


### 2. 动态规划的更优解

动态规划五部曲:

1.确定dp[j]下标及含义:定义dp[j]为使用字符串字典的[0,i]字符串可以拼成 s 的前 j 个字符串s[0:j)的长度

2.确定递推公式

定义当前遍历的物品字符串wordDict[i]的长度为len, 以及接下来要拼接的位置idx为`idx=max(dp[j], dp[j-len])`,当且仅当:

① 拼接完这个物品字符串后长度刚好为j`idx+len==j`

②当前物品字符串wordDict[i]完全匹配接下来要拼接的每个位置: `s[idx:idx+len)=str[i][0:len)`

上述两个条件都满足时更新`dp[j]=dp[idx]+len`

3.确定遍历顺序,上面强调过了,这是一个排列问题,for的遍历顺序为先背包后物品

4.初始化:全部初始化为0

5.举例推导

AC代码:
```
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
int dp[305]={0};
for(int j=1; j<=s.length(); j++){
for(int i=0; i<wordDict.size(); i++){
int idx;
if(j<=wordDict[i].length()){
idx = dp[j];
}
else{
idx = max(dp[j], dp[j-wordDict[i].length()]);
}
if(idx+wordDict[i].length() != j) continue;
bool flag = true;
for(int index=idx, z=0; z<wordDict[i].length(); index++, z++){
if(s[index] != wordDict[i][z]){
flag = false;
break;
}
}
if(flag){ // 如果匹配,就没必要继续看接下来的物品了,因为接下来无论用哪个物品拼接,长度都会大于j
dp[j] = idx+wordDict[i].length();
break;
}
}
}
if(dp[s.length()] == s.length()) return true;
return false;
}
};
```
时间复杂度:O(n*m*p),其中n为s的长度,m为字符串字典的长度,p为字符串字典中的字符串的长度

空间复杂度:O(n)

## 总结

本题和我们之前讲解回溯专题的[回溯算法:分割回文串](https://programmercarl.com/0131.分割回文串.html)非常像,所以我也给出了对应的回溯解法。
Expand Down