题目
分析
本题可看出为完全背包问题。关键是dp[i]表示什么?dp[i] == true 表示长度为 i 的字符串可以词典中的单词完全划分。
确定递推公式 :
if([j, i] 这个区间的子串出现在字典里 && dp[j]是true) 那么 dp[i] = true。
初始化:
为了不使初始化覆盖结果,所以dp数组初始化为false,但dp[0]为true,否则后面将全为false。
确定遍历顺序:
完全背包的题目可以先遍历物品再遍历背包或者反过来,但要保证内循环为正序(因为可以重复取同一物品)。但是本题有特殊性,本题的字符串为背包,词典中的单词为物品。应该先遍历背包再遍历物品,因为字符串本身是由词典中的单词按照特定顺序组成的,说白了,就是把物品装进背包是有顺序的。
代码
1 class Solution { 2 public: 3 bool wordBreak(string s, vector<string>& wordDict) { 4 unordered_set<string>st(wordDict.begin(),wordDict.end());//去重加查找单词 5 vector<bool>dp(s.length()+1,false); 6 dp[0] = true;//若不设为真,后面全为false 7 //先遍历背包再遍历物品 8 for(int i = 1;i <= s.length();i++){ 9 for(int j = 0;j < i;j++){ 10 string word = s.substr(j,i-j); 11 if(st.find(word)!=st.end() && dp[j] == true) 12 dp[i] = true; 13 } 14 } 15 return dp[s.length()]; 16 } 17 };
采用回溯+记忆化数组
class Solution { public: bool backtracking(const string &s,const unordered_set<string> &st,vector<int> &visit,int startIndex){ if(startIndex >= s.size()){ return true; } if(visit[startIndex] != -1) return visit[startIndex]; //若被搜索过,直接返回结果 //遍历所有分割方式 for(int i = startIndex;i < s.size();i++){ if(st.find(s.substr(startIndex,i-startIndex+1)) != st.end() && backtracking(s,st,visit,i+1)){ visit[startIndex] = 1; return true; } } //所有分割方式遍历不能返回true,就意味着找不到分割方式使得字符串拆分成字典中的单词 visit[startIndex] = 0;//以startIndex开始划分的子串不可拆分 return false; } bool wordBreak(string s, vector<string>& wordDict) { unordered_set<string>st(wordDict.begin(),wordDict.end());//去重加查找单词 vector<int>visit(s.size(),-1);//记忆化搜索 return backtracking(s,st,visit,0); } };