leetcode 95. Unique Binary Search Trees II、140. Word Break II(自顶向下解决问题)
思考自顶向下解决问题的模式
95. Unique Binary Search Trees
题目链接
leetcode 95. Unique Binary Search Trees
题意
给定一个整数n,输出把1到n所有值作为树结点的所有可能的二叉搜索树的组合。
思路
二叉搜索树的定义:左边结点的值 < 根节点的值 < 右边结点的值。那么根据根节点的不同,1到n的所有值尽可能作为根节点。这里我们不妨假设k(1<=k<=n)为某一颗二叉搜索树的根节点。那么左子树的根节点的可能值为[1,k-1],右子树的根节点的所有可能值为[k+1,n]。
难点在于怎么在[1,k-1],[k+1,n]继续遍历子树的所有左右子树,直接列举所有情况显然是不现实的,这时候我们采用自顶向下的思想,将[1,k-1],[k+1,n]的所有情况放在栈(存储子问题用到的临时变量)中,最后逐步合并所有可能的情况即可。
class Solution {
public:
//分治算法
vector<TreeNode*> generateTrees(int n){
vector<TreeNode*> ans;
if(n==0) return ans;
return solve(1,n);
}
vector<TreeNode*> solve(int l,int r){
vector<TreeNode*> ans;//通过栈区的临时变量存储左右根节点的信息
if(l>r){
ans.push_back(NULL);
return ans;
}
for(int i=l;i<=r;i++){
// TreeNode* root=new TreeNode(i);
vector<TreeNode*> left=solve(l,i-1);
vector<TreeNode*> right=solve(i+1,r);
//自顶向下思考问题
for(TreeNode * le: left){
for(TreeNode *ri:right){
TreeNode* root=new TreeNode(i);
root->left=le;
root->right=ri;
ans.push_back(root);//存储子问题的所有可能状态
}
}
}
return ans;
}
140. Word Break II
题目链接
题意
给定一个字典,判断一个字符串能被断句成多少种可能的情况
思路
- 首先利用一个set保存词典里面所有出现的词,这样在后续断句的时候能以O(1)的复杂度判断单词是否在词典中
- 然后就是断句,给定一个字符串s = “catsanddog”,给定词典 wordDict = [“cat”, “cats”, “and”, “sand”, “dog”],如何寻求所有可能的断句方式?我们可以采用类似上面一题的思想,将这个单词先分成左右两部分,比如将s分成"cat"和"sanddog",那么原问题就变成判断"cat"是否在词典wordDict中,以及"sanddog"的子字符串是否在词典中,这样依次类推下去。
- 对于本题还要采用记忆化搜索剪枝(用一个map存储一下已经遍历字符串的结果),避免右边部分字符串的子问题反复判断是否在词典中
class Solution {
public:
//好题
//学习自顶向下思考问题的模式
//给定词库 给样例单词做分词
//暴力O(N^2M)
//如何高效判断单词的匹配是否正确
//怎么确定分词位置?
//与判断不同类型的二叉树的题 处理方法比较类似 有点自顶向下的思想
//新建一个vector 然后从初始点一步步包括子问题
unordered_map<string,vector<string>> ump;//记忆化搜索 剪枝
vector<string> wordBreak(string s, vector<string>& wordDict) {
vector<string> ans;
if(!s.size() || !wordDict.size()) return ans;
set<string> S;
for(string str:wordDict){
if(!S.count(str)){
S.insert(str);
}
}
return solve(s,S);
}
bool Containr(set<string> S,string str){
for(int i=1;i<=str.size();i++){
string tmp=str.substr(0,i);
if(S.count(tmp)) return true;
}
return false;
}
vector<string> solve(string s, set<string> S){
//记忆化搜索
if(ump.count(s)) return ump[s];
vector<string> ans;//自顶向下,ans放在栈中,存放临时变量
if(S.count(s)) ans.push_back(s);//全部包含 单独处理
for(int i=1;i<s.size();i++){//长度
string left=s.substr(0,i);
string right=s.substr(i);
if(S.count(left) && Containr(S,right) ){//左边包含 右边也有可能包含
//子问题
for(string r: solve(right,S)){//仅仅右边字符串 符合情况的所有字符串情况
ans.push_back(left+" "+r);
}
}
}
//存储已经找过的字符串
ump[s]=ans;
return ans;
}
};
本题思路参考:https://leetcode.com/problems/word-break-ii/discuss/44179/Slightly-modified-DP-Java-solution