139. 单词拆分
链接
文章链接
题目链接
第一想法
我的想法可能复杂了,但是这估计是我能想到的最好的方法了,首先是dp数组,我的dp数组的含义为dp[j]为字符串长为j是是否等于s的前j的字符串,所以我默认初始化都为false.但是dp[0]=true,默认空字符为true.之后递推公式为dp[j] = str == wordDict[i] ? dp[j - wordDict[i].length] : dp[j]
,需要判断当前的字符串是否能按照序列匹配s中的一部分,如果不可以则返回原来的dp[j];如果能匹配,则返回 dp[j - wordDict[i].length](长度为j - wordDict[i].length的匹配情况)。最后是遍历顺序,这道题,刚开始没注意,结果发现怎么都允许不成功,后来发现这是个排列问题,所以需要先遍历背包再遍历物品,代码如下:
function wordBreak(s: string, wordDict: string[]): boolean {
let dp: boolean[] = new Array(s.length + 1).fill(false)//dp数组
dp[0] = true//初始化
for (let j = 0; j <= s.length; j++) {//先遍历背包
for (let i = 0; i < wordDict.length; i++) {//物品(这里的物品是字符串的长度)
if (dp[j] || j < wordDict[i].length) continue //如果当前dp[j]是true的话就不能判断了 j < wordDict[i].length时不应该递推
let str: string = s.substring(j - wordDict[i].length, j)//获取s的j - wordDict[i].length到 j字符串
dp[j] = str == wordDict[i] ? dp[j - wordDict[i].length] : dp[j]//递推公式
}
}
return dp[s.length]
}
看完文章后的想法
文章的想法和我的几乎是完全不一样的,原因在于从最开始的dp数组的理解就不一样。文章的理解为dp[j]为长度为j的字符串能否被分割为wordDict中的元素。初始化和我上述是一样的,dp[0]=true,其余都是0;递推公式,递推公式为 if (wordDict.includes(str) && dp[i]) dp[j] = true
,只有当前字符串属于wordDict中并且除去当前字符串之前的字符串也是正确拼接的(也是属于wordDict的)。遍历顺序,还是先背包再物品,代码如下:
function wordBreak(s: string, wordDict: string[]): boolean {
let dp: boolean[] = new Array(s.length + 1).fill(false)//dp
dp[0] = true
for (let j = 0; j <= s.length; j++) {//背包 s是背包
for (let i = 0; i < j; i++) {//i是从哪开始截取
let str: string = s.slice(i, j)//截取字符串
if (wordDict.includes(str) && dp[i]) dp[j] = true//字符串属于wordDict并且拼接之前的d[i]也是为true的
}
}
return dp[s.length]
}
思考
这道题是挺难的,写了一段的时间,确实挺难理解的,但是靠自己写出来了,虽然和文章的写法不太一样,但是也是不错的。文章的写法我认为是比较难理解的
多重背包理论基础
文章链接
多重背包其实就是0-1背包的拓展,每个物品不是无数的,都是有一定的数量的,解决方法就是把数量多的物品看成n格价值相同的物品,例如: 背包最大重量为10。
物品为:
重量 | 价值 | 数量 | |
---|---|---|---|
物品0 | 1 | 15 | 2 |
物品1 | 3 | 20 | 3 |
物品2 | 4 | 30 | 2 |
所以可以看作为:
重量 | 价值 | 数量 | |
---|---|---|---|
物品0 | 1 | 15 | 1 |
物品0 | 1 | 15 | 1 |
物品1 | 3 | 20 | 1 |
物品1 | 3 | 20 | 1 |
物品1 | 3 | 20 | 1 |
物品2 | 4 | 30 | 1 |
物品2 | 4 | 30 | 1 |
这道题的解法如下:
function foo1(weight: number[], value: number[], nums: number[], size: number) {
let dp: number[] = new Array(size + 1).fill(0)//dp
for (let i = 0; i < nums.length; i++) {//将物品有n个转变为有n个相同的物品
while (nums[i] > 1) {
weight.push(weight[i])
value.push(value[i])
nums[i]--
}
}
for (let i = 0; i < weight.length; i++) {//物品
for (let j = size; j >= weight[i]; j--) {//背包
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i])//递推公式
}
}
console.log(dp)
console.log(dp[size])
}
foo1([1, 3, 4], [15, 20, 30], [2, 3, 2], 10)
今日总结
今天就一道题,但是这道题也是比较难的,后面讲了多重背包的理论,发现不难