一、LeetCode139.单词拆分
题目链接/文章讲解/视频讲解:https://programmercarl.com/0139.%E5%8D%95%E8%AF%8D%E6%8B%86%E5%88%86.html
状态:已解决
1.思路
单词明显就是物品,字符串s明显就是背包,那么问题就变成了物品能不能把背包装满。
(1)确定dp数组以及下标的含义:
dp[i]:字符串长度为i,dp[i]为true,代表长度为 i 的子串可以被单词组成。
(2)确定递推公式:
对于dp[i],如果一个长为 wordDict[j].size() 的物品(单词)是s在[i-wordDict[j],i]这个范围内的子串,那么dp[i] = dp[j]的状态。
(3) 初始化dp数组:
dp[0]表示如果字符串为空的话,说明出现在字典里。(实际题目中说了“给定一个非空字符串 s” 所以测试数据中不会出现i为0的情况,那么dp[0]初始为true完全就是为了推导公式。)
下标非0的dp[i]初始化为false,只要没有被覆盖说明都是不可拆分为一个或多个在字典中出现的单词。
(4)确定遍历顺序:
题目中说是拆分为一个或多个在字典中出现的单词,所以这是完全背包。本题其实我们求的是排列数, 拿 s = "applepenapple", wordDict = ["apple", "pen"] 举例。"apple", "pen" 是物品,那么我们要求 物品的组合一定是 "apple" + "pen" + "apple" 才能组成 "applepenapple"。"apple" + "apple" + "pen" 或者 "pen" + "apple" + "apple" 是不可以的,那么我们就是强调物品之间顺序。所以说,本题一定是 先遍历 背包,再遍历物品。且内外层都是从前往后遍历。
(5)举例推导dp:
以输入: s = "leetcode", wordDict = ["leet", "code"]为例,dp状态如图:
dp[s.size()]就是最终结果。
2.代码实现
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
vector<bool> dp(s.size()+1,false);
dp[0] = true;
for(int i=0;i<=s.size();i++){
for(int j=0;j<wordDict.size();j++){
if(wordDict[j].size() <= i){
string word = s.substr(i-wordDict[j].size(),wordDict[j].size());
if(!dp[i] && word == wordDict[j]){
dp[i] = dp[i-wordDict[j].size()];
}
}
}
}
//for(int i=0;i<=s.size();i++) cout<<dp[i]<<" ";
return dp[s.size()];
}
};
时间复杂度:O(n^3) :求子串是一个O(n)的复杂度
空间复杂度:O(n)